summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy2
-rw-r--r--.gitignore5
-rw-r--r--.travis.yml240
-rw-r--r--ARCHITECTURE.md46
-rw-r--r--CMakeLists.txt62
-rw-r--r--CODE-OF-CONDUCT.md5
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--DEVELOPING.md123
-rw-r--r--INSTALL.md19
-rw-r--r--Makefile134
-rw-r--r--README.md13
-rw-r--r--benchmark/api/query.benchmark.cpp25
-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.cpp69
-rw-r--r--benchmark/function/composite_function.benchmark.cpp76
-rw-r--r--benchmark/function/source_function.benchmark.cpp70
-rw-r--r--benchmark/parse/tile_mask.benchmark.cpp38
-rw-r--r--benchmark/parse/vector_tile.benchmark.cpp28
-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/render.cpp41
-rw-r--r--circle.yml527
-rw-r--r--cloudformation/travis.template34
-rw-r--r--cmake/benchmark-files.cmake14
-rw-r--r--cmake/benchmark.cmake12
-rw-r--r--cmake/core-files.cmake176
-rw-r--r--cmake/core.cmake6
-rw-r--r--cmake/executable.xcscheme (renamed from platform/macos/scripts/executable.xcscheme)46
-rw-r--r--cmake/glfw.cmake32
-rw-r--r--cmake/library.xcscheme (renamed from platform/macos/scripts/library.xcscheme)26
-rw-r--r--cmake/loop-darwin.cmake2
-rw-r--r--cmake/loop-uv.cmake6
-rw-r--r--cmake/mbgl.cmake32
-rw-r--r--cmake/node.cmake47
-rw-r--r--cmake/node.xcscheme (renamed from platform/macos/scripts/node.xcscheme)38
-rw-r--r--cmake/offline.cmake15
-rw-r--r--cmake/render.cmake21
-rw-r--r--cmake/test-files.cmake21
-rw-r--r--cmake/test.cmake14
-rw-r--r--cmake/xcode.cmake75
-rw-r--r--include/mbgl/actor/actor.hpp (renamed from src/mbgl/actor/actor.hpp)27
-rw-r--r--include/mbgl/actor/actor_ref.hpp (renamed from src/mbgl/actor/actor_ref.hpp)27
-rw-r--r--include/mbgl/actor/mailbox.hpp6
-rw-r--r--include/mbgl/actor/message.hpp108
-rw-r--r--include/mbgl/actor/scheduler.hpp15
-rw-r--r--include/mbgl/annotation/annotation.hpp42
-rw-r--r--include/mbgl/map/map.hpp89
-rw-r--r--include/mbgl/map/mode.hpp11
-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)6
-rw-r--r--include/mbgl/renderer/query.hpp (renamed from include/mbgl/map/query.hpp)12
-rw-r--r--include/mbgl/renderer/renderer.hpp55
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp (renamed from include/mbgl/map/backend.hpp)46
-rw-r--r--include/mbgl/renderer/renderer_frontend.hpp31
-rw-r--r--include/mbgl/storage/default_file_source.hpp22
-rw-r--r--include/mbgl/storage/offline.hpp4
-rw-r--r--include/mbgl/storage/online_file_source.hpp11
-rw-r--r--include/mbgl/storage/resource.hpp7
-rw-r--r--include/mbgl/storage/resource_transform.hpp26
-rw-r--r--include/mbgl/storage/response.hpp10
-rw-r--r--include/mbgl/style/conversion.hpp1
-rw-r--r--include/mbgl/style/conversion/coordinate.hpp37
-rw-r--r--include/mbgl/style/conversion/filter.hpp2
-rw-r--r--include/mbgl/style/conversion/function.hpp4
-rw-r--r--include/mbgl/style/conversion/layer.hpp23
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp202
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp.ejs8
-rw-r--r--include/mbgl/style/conversion/property_setter.hpp34
-rw-r--r--include/mbgl/style/conversion/source.hpp47
-rw-r--r--include/mbgl/style/data_driven_property_value.hpp8
-rw-r--r--include/mbgl/style/function/camera_function.hpp5
-rw-r--r--include/mbgl/style/function/composite_function.hpp7
-rw-r--r--include/mbgl/style/function/exponential_stops.hpp22
-rw-r--r--include/mbgl/style/function/interval_stops.hpp18
-rw-r--r--include/mbgl/style/function/source_function.hpp3
-rw-r--r--include/mbgl/style/image.hpp18
-rw-r--r--include/mbgl/style/layer.hpp49
-rw-r--r--include/mbgl/style/layers/background_layer.hpp40
-rw-r--r--include/mbgl/style/layers/circle_layer.hpp102
-rw-r--r--include/mbgl/style/layers/custom_layer.hpp29
-rw-r--r--include/mbgl/style/layers/fill_extrusion_layer.hpp72
-rw-r--r--include/mbgl/style/layers/fill_layer.hpp72
-rw-r--r--include/mbgl/style/layers/layer.hpp.ejs24
-rw-r--r--include/mbgl/style/layers/line_layer.hpp104
-rw-r--r--include/mbgl/style/layers/raster_layer.hpp72
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp160
-rw-r--r--include/mbgl/style/light.hpp16
-rw-r--r--include/mbgl/style/light.hpp.ejs16
-rw-r--r--include/mbgl/style/position.hpp2
-rw-r--r--include/mbgl/style/property_value.hpp6
-rw-r--r--include/mbgl/style/source.hpp31
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp16
-rw-r--r--include/mbgl/style/sources/image_source.hpp41
-rw-r--r--include/mbgl/style/sources/raster_source.hpp17
-rw-r--r--include/mbgl/style/sources/vector_source.hpp17
-rw-r--r--include/mbgl/style/style.hpp79
-rw-r--r--include/mbgl/style/transition_options.hpp9
-rw-r--r--include/mbgl/style/types.hpp5
-rw-r--r--include/mbgl/tile/tile_id.hpp (renamed from src/mbgl/tile/tile_id.hpp)66
-rw-r--r--include/mbgl/util/constants.hpp6
-rw-r--r--include/mbgl/util/convert.hpp6
-rw-r--r--include/mbgl/util/image.hpp49
-rw-r--r--include/mbgl/util/immutable.hpp133
-rw-r--r--include/mbgl/util/indexed_tuple.hpp7
-rw-r--r--include/mbgl/util/interpolate.hpp6
-rw-r--r--include/mbgl/util/noncopyable.hpp8
-rw-r--r--include/mbgl/util/projection.hpp22
-rw-r--r--include/mbgl/util/range.hpp6
-rw-r--r--include/mbgl/util/run_loop.hpp21
-rw-r--r--include/mbgl/util/size.hpp4
-rw-r--r--include/mbgl/util/string.hpp25
-rw-r--r--include/mbgl/util/thread.hpp161
-rw-r--r--include/mbgl/util/tileset.hpp19
-rw-r--r--include/mbgl/util/unitbezier.hpp8
-rw-r--r--include/mbgl/util/util.hpp7
-rw-r--r--include/mbgl/util/work_task.hpp3
-rw-r--r--include/mbgl/util/work_task_impl.hpp44
m---------mapbox-gl-js0
-rw-r--r--package.json13
-rw-r--r--platform/android/.gitignore1
-rw-r--r--platform/android/CHANGELOG.md7
-rw-r--r--platform/android/MapboxGLAndroidSDK/.gitignore2
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle19
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle59
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties4
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml37
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml44
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java3
-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/IconFactory.java2
-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.java3
-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/constants/MapboxConstants.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java293
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java87
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java86
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java471
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java25
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java43
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java13
-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.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java397
-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.java283
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java111
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java244
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java39
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java266
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java82
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java81
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java22
-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.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java145
-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/widgets/CompassView.java63
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java24
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java260
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java6
-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/CircleLayer.java12
-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/Property.java103
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java108
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java24
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java137
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java27
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml8
-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-xxxhdpi/mapbox_infowindow_icon_bg.9.pngbin928 -> 0 bytes
-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_view_image_marker.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java)9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java81
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java)10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java)0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker)0
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/.gitignore2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/build.gradle14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle11
-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.java16
-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/maps/widgets/MyLocationViewTest.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java48
-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.java54
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java190
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java533
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java43
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml89
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java2
-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/BulkMarkerActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java7
-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.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java174
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java137
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java19
-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.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java288
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java192
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java20
-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/MapChangeActivity.java111
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java24
-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/SimpleMapActivity.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java146
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java131
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java148
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java2
-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.java41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java98
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java216
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java171
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java3
-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/OfflineListRegionsDialog.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java2
-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/utils/TokenUtils.java37
-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-mdpi/southeast_radar_0.pngbin0 -> 172489 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.pngbin0 -> 177163 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.pngbin0 -> 179236 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.pngbin0 -> 177074 bytes
-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_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.xml17
-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_debug_mode.xml121
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml36
-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_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_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_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_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.xml8
-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_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.xml95
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml65
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml177
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml65
-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/activity/SimpleWearMapActivity.java72
-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.md18
-rw-r--r--platform/android/bitrise.yml235
-rw-r--r--platform/android/build.gradle1
-rw-r--r--platform/android/config.cmake31
-rw-r--r--platform/android/dependencies.gradle26
-rw-r--r--platform/android/gradle-lint.gradle30
-rw-r--r--platform/android/scripts/generate-style-code.js6
-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.cpp117
-rw-r--r--platform/android/src/android_renderer_frontend.hpp49
-rw-r--r--platform/android/src/asset_manager_file_source.cpp18
-rw-r--r--platform/android/src/asset_manager_file_source.hpp5
-rw-r--r--platform/android/src/conversion/constant.hpp1
-rw-r--r--platform/android/src/example_custom_layer.cpp8
-rw-r--r--platform/android/src/file_source.cpp22
-rw-r--r--platform/android/src/file_source.hpp8
-rw-r--r--platform/android/src/geometry/lat_lng_quad.cpp39
-rw-r--r--platform/android/src/geometry/lat_lng_quad.hpp30
-rw-r--r--platform/android/src/http_file_source.cpp4
-rwxr-xr-xplatform/android/src/jni.cpp10
-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_renderer.cpp200
-rw-r--r--platform/android/src/map_renderer.hpp126
-rw-r--r--platform/android/src/map_renderer_runnable.cpp49
-rw-r--r--platform/android/src/map_renderer_runnable.hpp50
-rwxr-xr-xplatform/android/src/native_map_view.cpp729
-rwxr-xr-xplatform/android/src/native_map_view.hpp107
-rw-r--r--platform/android/src/run_loop.cpp24
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.cpp110
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.hpp61
-rw-r--r--platform/android/src/style/android_conversion.hpp9
-rw-r--r--platform/android/src/style/conversion/latlngquad.hpp24
-rw-r--r--platform/android/src/style/conversion/transition_options.hpp1
-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/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.cpp9
-rw-r--r--platform/android/src/style/layers/custom_layer.hpp4
-rw-r--r--platform/android/src/style/layers/layer.cpp96
-rw-r--r--platform/android/src/style/layers/layers.cpp82
-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/geojson_source.cpp10
-rw-r--r--platform/android/src/style/sources/geojson_source.hpp2
-rw-r--r--platform/android/src/style/sources/image_source.cpp72
-rw-r--r--platform/android/src/style/sources/image_source.hpp38
-rw-r--r--platform/android/src/style/sources/raster_source.cpp4
-rw-r--r--platform/android/src/style/sources/raster_source.hpp2
-rw-r--r--platform/android/src/style/sources/source.cpp14
-rw-r--r--platform/android/src/style/sources/source.hpp10
-rw-r--r--platform/android/src/style/sources/sources.cpp44
-rw-r--r--platform/android/src/style/sources/sources.hpp10
-rw-r--r--platform/android/src/style/sources/unknown_source.cpp4
-rw-r--r--platform/android/src/style/sources/unknown_source.hpp2
-rw-r--r--platform/android/src/style/sources/vector_source.cpp10
-rw-r--r--platform/android/src/style/sources/vector_source.hpp2
-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.ejs10
-rw-r--r--platform/darwin/scripts/generate-style-code.js14
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.h45
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.mm32
-rw-r--r--platform/darwin/src/MGLConversion.h8
-rw-r--r--platform/darwin/src/MGLGeometry.h54
-rw-r--r--platform/darwin/src/MGLGeometry_Private.h15
-rw-r--r--platform/darwin/src/MGLImageSource.h94
-rw-r--r--platform/darwin/src/MGLImageSource.mm92
-rw-r--r--platform/darwin/src/MGLLight.h2
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.h23
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.mm12
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h105
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm163
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm2
-rw-r--r--platform/darwin/src/MGLOfflineStorage.h2
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm20
-rw-r--r--platform/darwin/src/MGLOpenGLStyleLayer.h3
-rw-r--r--platform/darwin/src/MGLOpenGLStyleLayer.mm37
-rw-r--r--platform/darwin/src/MGLRendererFrontend.h70
-rw-r--r--platform/darwin/src/MGLShapeSource.mm4
-rw-r--r--platform/darwin/src/MGLSource.h2
-rw-r--r--platform/darwin/src/MGLSource.mm12
-rw-r--r--platform/darwin/src/MGLSource_Private.h3
-rw-r--r--platform/darwin/src/MGLStyle.h85
-rw-r--r--platform/darwin/src/MGLStyle.mm116
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm18
-rw-r--r--platform/darwin/src/MGLStyleLayer_Private.h6
-rw-r--r--platform/darwin/src/MGLStyle_Private.h9
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h190
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm96
-rw-r--r--platform/darwin/src/MGLVectorSource.mm4
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.h14
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.m10
-rw-r--r--platform/darwin/src/headless_backend_cgl.cpp2
-rw-r--r--platform/darwin/src/http_file_source.mm4
-rw-r--r--platform/darwin/src/nsthread.mm3
-rw-r--r--platform/darwin/src/run_loop.cpp20
-rw-r--r--platform/darwin/test/MGLCircleStyleLayerTests.mm42
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift14
-rw-r--r--platform/darwin/test/MGLGeometryTests.mm19
-rw-r--r--platform/darwin/test/MGLImageSourceTests.m42
-rw-r--r--platform/darwin/test/MGLLightTest.mm20
-rw-r--r--platform/darwin/test/MGLLightTest.mm.ejs10
-rw-r--r--platform/darwin/test/MGLLineStyleLayerTests.mm37
-rw-r--r--platform/darwin/test/MGLStyleTests.mm18
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm165
-rw-r--r--platform/darwin/test/Media.xcassets/RadarImage.imageset/Contents.json21
-rw-r--r--platform/darwin/test/Media.xcassets/RadarImage.imageset/radar.pngbin0 -> 44094 bytes
-rw-r--r--platform/default/asset_file_source.cpp20
-rw-r--r--platform/default/async_task.cpp2
-rw-r--r--platform/default/bidi.cpp6
-rw-r--r--platform/default/default_file_source.cpp210
-rw-r--r--platform/default/http_file_source.cpp4
-rw-r--r--platform/default/image.cpp2
-rw-r--r--platform/default/jpeg_reader.cpp8
-rw-r--r--platform/default/local_file_source.cpp15
-rw-r--r--platform/default/mbgl/gl/headless_backend.cpp48
-rw-r--r--platform/default/mbgl/gl/headless_backend.hpp19
-rw-r--r--platform/default/mbgl/gl/headless_display.hpp16
-rw-r--r--platform/default/mbgl/gl/headless_frontend.cpp86
-rw-r--r--platform/default/mbgl/gl/headless_frontend.hpp48
-rw-r--r--platform/default/mbgl/gl/offscreen_view.cpp64
-rw-r--r--platform/default/mbgl/gl/offscreen_view.hpp28
-rw-r--r--platform/default/mbgl/map/map_snapshotter.cpp76
-rw-r--r--platform/default/mbgl/map/map_snapshotter.hpp40
-rw-r--r--platform/default/mbgl/storage/offline.cpp33
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp195
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp1
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp89
-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/online_file_source.cpp83
-rw-r--r--platform/default/png_reader.cpp2
-rw-r--r--platform/default/run_loop.cpp13
-rw-r--r--platform/default/sqlite3.cpp17
-rw-r--r--platform/default/thread_local.cpp66
-rw-r--r--platform/default/timer.cpp2
-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.cpp150
-rw-r--r--platform/glfw/glfw_view.hpp33
-rw-r--r--platform/glfw/main.cpp103
-rw-r--r--platform/glfw/ny_route.hpp104
-rw-r--r--platform/glfw/settings_json.cpp3
-rw-r--r--platform/glfw/settings_json.hpp1
-rw-r--r--platform/ios/CHANGELOG.md36
-rw-r--r--platform/ios/DEVELOPING.md7
-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/app/MBXSnapshotsViewController.h5
-rw-r--r--platform/ios/app/MBXSnapshotsViewController.m66
-rw-r--r--platform/ios/app/MBXViewController.m84
-rw-r--r--platform/ios/app/Main.storyboard76
-rw-r--r--platform/ios/bitrise.yml2
-rw-r--r--platform/ios/config.cmake9
-rw-r--r--platform/ios/docs/guides/Adding Points to a Map.md1
-rw-r--r--platform/ios/docs/guides/For Style Authors.md10
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj49
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme2
-rw-r--r--platform/ios/jazzy.yml5
-rw-r--r--platform/ios/src/MGLMapView.h32
-rw-r--r--platform/ios/src/MGLMapView.mm131
-rw-r--r--platform/ios/src/MGLMapView_Private.h3
-rw-r--r--platform/ios/src/Mapbox.h2
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.h6
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.mm27
-rw-r--r--platform/linux/README.md9
-rw-r--r--platform/linux/config.cmake9
-rwxr-xr-xplatform/linux/scripts/coveralls.sh15
-rw-r--r--platform/linux/src/headless_backend_egl.cpp2
-rw-r--r--platform/linux/src/headless_backend_glx.cpp10
-rw-r--r--platform/linux/src/headless_display_egl.cpp3
-rw-r--r--platform/linux/src/headless_display_glx.cpp5
-rw-r--r--platform/macos/CHANGELOG.md32
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/Contents.json6
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/Contents.json21
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/southeast_radar_0.pngbin0 -> 172489 bytes
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/Contents.json21
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/southeast_radar_1.pngbin0 -> 177163 bytes
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/Contents.json21
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/southeast_radar_2.pngbin0 -> 179236 bytes
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/Contents.json21
-rw-r--r--platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/southeast_radar_3.pngbin0 -> 177074 bytes
-rw-r--r--platform/macos/app/Base.lproj/MainMenu.xib22
-rw-r--r--platform/macos/app/Base.lproj/MapDocument.xib6
-rw-r--r--platform/macos/app/MapDocument.m158
-rw-r--r--platform/macos/config.cmake22
-rw-r--r--platform/macos/docs/guides/For Style Authors.md10
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj24
-rwxr-xr-xplatform/macos/scripts/create_scheme.sh40
-rw-r--r--platform/macos/src/MGLMapView.h12
-rw-r--r--platform/macos/src/MGLMapView.mm113
-rw-r--r--platform/macos/src/MGLMapView_Private.h3
-rw-r--r--platform/macos/src/Mapbox.h2
-rw-r--r--platform/macos/src/NSImage+MGLAdditions.h4
-rw-r--r--platform/macos/src/NSImage+MGLAdditions.mm33
-rw-r--r--platform/node/CHANGELOG.md19
-rw-r--r--platform/node/README.md6
-rw-r--r--platform/node/bitrise.yml2
-rw-r--r--platform/node/index.js27
-rwxr-xr-xplatform/node/scripts/after_success.sh9
-rw-r--r--platform/node/src/node_conversion.hpp8
-rw-r--r--platform/node/src/node_geojson.hpp8
-rw-r--r--platform/node/src/node_map.cpp208
-rw-r--r--platform/node/src/node_map.hpp20
-rw-r--r--platform/node/src/node_request.cpp10
-rw-r--r--platform/node/src/node_request.hpp6
-rw-r--r--platform/node/test/ignores.json59
-rw-r--r--platform/node/test/js/map.test.js2
-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.js71
-rw-r--r--platform/qt/app/mapwindow.cpp54
-rw-r--r--platform/qt/app/mapwindow.hpp1
-rw-r--r--platform/qt/config.cmake4
-rw-r--r--platform/qt/include/qmapbox.hpp35
-rw-r--r--platform/qt/include/qmapboxgl.hpp12
-rw-r--r--platform/qt/qt.cmake17
-rw-r--r--platform/qt/src/http_request.cpp9
-rw-r--r--platform/qt/src/qmapbox.cpp12
-rw-r--r--platform/qt/src/qmapboxgl.cpp259
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp18
-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.hpp7
-rw-r--r--platform/qt/src/qt_image.cpp (renamed from platform/qt/src/image.cpp)0
-rw-r--r--platform/qt/src/run_loop.cpp16
-rw-r--r--platform/qt/src/sqlite3.cpp21
-rw-r--r--platform/qt/src/thread.cpp19
-rw-r--r--platform/qt/src/thread_local.cpp49
-rw-r--r--platform/qt/test/qmapboxgl.cpp3
-rwxr-xr-xscripts/check-cxx11abi.sh4
-rwxr-xr-xscripts/clang-tools.sh80
-rwxr-xr-xscripts/generate-shaders.js98
-rw-r--r--scripts/generate-style-code.js12
-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/travis_helper.sh73
-rwxr-xr-xscripts/travis_setup.sh77
-rw-r--r--scripts/valgrind.sup293
-rw-r--r--src/csscolorparser/csscolorparser.cpp9
-rw-r--r--src/mbgl/actor/mailbox.cpp28
-rw-r--r--src/mbgl/actor/message.hpp48
-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.hpp26
-rw-r--r--src/mbgl/algorithm/update_renderables.hpp9
-rw-r--r--src/mbgl/algorithm/update_tile_masks.hpp128
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp191
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp40
-rw-r--r--src/mbgl/annotation/annotation_source.cpp13
-rw-r--r--src/mbgl/annotation/annotation_source.hpp11
-rw-r--r--src/mbgl/annotation/annotation_tile.cpp101
-rw-r--r--src/mbgl/annotation/annotation_tile.hpp49
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.cpp8
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/line_annotation_impl.cpp8
-rw-r--r--src/mbgl/annotation/line_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/render_annotation_source.cpp69
-rw-r--r--src/mbgl/annotation/render_annotation_source.hpp38
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.cpp4
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.hpp8
-rw-r--r--src/mbgl/annotation/style_sourced_annotation_impl.cpp43
-rw-r--r--src/mbgl/annotation/style_sourced_annotation_impl.hpp19
-rw-r--r--src/mbgl/annotation/symbol_annotation_impl.cpp2
-rw-r--r--src/mbgl/annotation/symbol_annotation_impl.hpp46
-rw-r--r--src/mbgl/geometry/anchor.hpp2
-rw-r--r--src/mbgl/geometry/binpack.hpp101
-rw-r--r--src/mbgl/geometry/feature_index.cpp101
-rw-r--r--src/mbgl/geometry/feature_index.hpp12
-rw-r--r--src/mbgl/gl/attribute.cpp30
-rw-r--r--src/mbgl/gl/attribute.hpp23
-rw-r--r--src/mbgl/gl/context.cpp93
-rw-r--r--src/mbgl/gl/context.hpp30
-rw-r--r--src/mbgl/gl/debugging.cpp8
-rw-r--r--src/mbgl/gl/debugging_extension.hpp14
-rw-r--r--src/mbgl/gl/gl.cpp3
-rw-r--r--src/mbgl/gl/index_buffer.hpp2
-rw-r--r--src/mbgl/gl/program.hpp12
-rw-r--r--src/mbgl/gl/renderbuffer.hpp16
-rw-r--r--src/mbgl/gl/texture.hpp20
-rw-r--r--src/mbgl/gl/types.hpp38
-rw-r--r--src/mbgl/gl/uniform.cpp93
-rw-r--r--src/mbgl/gl/uniform.hpp35
-rw-r--r--src/mbgl/gl/value.cpp74
-rw-r--r--src/mbgl/gl/value.hpp37
-rw-r--r--src/mbgl/gl/vertex_buffer.hpp2
-rw-r--r--src/mbgl/layout/symbol_instance.cpp25
-rw-r--r--src/mbgl/layout/symbol_instance.hpp13
-rw-r--r--src/mbgl/layout/symbol_layout.cpp264
-rw-r--r--src/mbgl/layout/symbol_layout.hpp31
-rw-r--r--src/mbgl/layout/symbol_projection.cpp358
-rw-r--r--src/mbgl/layout/symbol_projection.hpp25
-rw-r--r--src/mbgl/map/backend.cpp58
-rw-r--r--src/mbgl/map/map.cpp738
-rw-r--r--src/mbgl/map/transform.cpp8
-rw-r--r--src/mbgl/map/transform_state.cpp16
-rw-r--r--src/mbgl/map/transform_state.hpp6
-rw-r--r--src/mbgl/map/update.hpp28
-rw-r--r--src/mbgl/map/zoom_history.hpp40
-rw-r--r--src/mbgl/programs/attributes.hpp8
-rw-r--r--src/mbgl/programs/binary_program.cpp6
-rw-r--r--src/mbgl/programs/binary_program.hpp2
-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.hpp16
-rw-r--r--src/mbgl/programs/debug_program.hpp3
-rw-r--r--src/mbgl/programs/extrusion_texture_program.hpp3
-rw-r--r--src/mbgl/programs/fill_extrusion_program.cpp20
-rw-r--r--src/mbgl/programs/fill_extrusion_program.hpp8
-rw-r--r--src/mbgl/programs/fill_program.cpp20
-rw-r--r--src/mbgl/programs/fill_program.hpp8
-rw-r--r--src/mbgl/programs/line_program.cpp38
-rw-r--r--src/mbgl/programs/line_program.hpp27
-rw-r--r--src/mbgl/programs/program.hpp4
-rw-r--r--src/mbgl/programs/segment.cpp7
-rw-r--r--src/mbgl/programs/segment.hpp5
-rw-r--r--src/mbgl/programs/symbol_program.cpp52
-rw-r--r--src/mbgl/programs/symbol_program.hpp236
-rw-r--r--src/mbgl/programs/uniforms.hpp15
-rw-r--r--src/mbgl/renderer/backend_scope.cpp (renamed from src/mbgl/map/backend_scope.cpp)6
-rw-r--r--src/mbgl/renderer/bucket.hpp21
-rw-r--r--src/mbgl/renderer/bucket_parameters.hpp1
-rw-r--r--src/mbgl/renderer/buckets/circle_bucket.cpp (renamed from src/mbgl/renderer/circle_bucket.cpp)12
-rw-r--r--src/mbgl/renderer/buckets/circle_bucket.hpp (renamed from src/mbgl/renderer/circle_bucket.hpp)2
-rw-r--r--src/mbgl/renderer/buckets/debug_bucket.cpp (renamed from src/mbgl/renderer/debug_bucket.cpp)4
-rw-r--r--src/mbgl/renderer/buckets/debug_bucket.hpp (renamed from src/mbgl/renderer/debug_bucket.hpp)0
-rw-r--r--src/mbgl/renderer/buckets/fill_bucket.cpp (renamed from src/mbgl/renderer/fill_bucket.cpp)12
-rw-r--r--src/mbgl/renderer/buckets/fill_bucket.hpp (renamed from src/mbgl/renderer/fill_bucket.hpp)1
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp (renamed from src/mbgl/renderer/fill_extrusion_bucket.cpp)12
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp (renamed from src/mbgl/renderer/fill_extrusion_bucket.hpp)1
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.cpp (renamed from src/mbgl/renderer/line_bucket.cpp)34
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.hpp (renamed from src/mbgl/renderer/line_bucket.hpp)6
-rw-r--r--src/mbgl/renderer/buckets/raster_bucket.cpp110
-rw-r--r--src/mbgl/renderer/buckets/raster_bucket.hpp41
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp (renamed from src/mbgl/renderer/symbol_bucket.cpp)23
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp (renamed from src/mbgl/renderer/symbol_bucket.hpp)30
-rw-r--r--src/mbgl/renderer/cross_faded_property_evaluator.cpp5
-rw-r--r--src/mbgl/renderer/data_driven_property_evaluator.hpp10
-rw-r--r--src/mbgl/renderer/frame_history.cpp10
-rw-r--r--src/mbgl/renderer/frame_history.hpp1
-rw-r--r--src/mbgl/renderer/group_by_layout.cpp14
-rw-r--r--src/mbgl/renderer/image_atlas.cpp60
-rw-r--r--src/mbgl/renderer/image_atlas.hpp51
-rw-r--r--src/mbgl/renderer/image_manager.cpp184
-rw-r--r--src/mbgl/renderer/image_manager.hpp91
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp115
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.hpp (renamed from src/mbgl/renderer/render_background_layer.hpp)16
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp120
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.hpp (renamed from src/mbgl/renderer/render_circle_layer.hpp)16
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp81
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp (renamed from src/mbgl/renderer/render_custom_layer.hpp)24
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp167
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp (renamed from src/mbgl/renderer/render_fill_extrusion_layer.hpp)18
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp205
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.hpp (renamed from src/mbgl/renderer/render_fill_layer.hpp)14
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp204
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.hpp (renamed from src/mbgl/renderer/render_line_layer.hpp)27
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.cpp155
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.hpp (renamed from src/mbgl/renderer/render_raster_layer.hpp)15
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp325
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.hpp (renamed from src/mbgl/renderer/render_symbol_layer.hpp)37
-rw-r--r--src/mbgl/renderer/paint_parameters.cpp98
-rw-r--r--src/mbgl/renderer/paint_parameters.hpp68
-rw-r--r--src/mbgl/renderer/paint_property_binder.hpp7
-rw-r--r--src/mbgl/renderer/painter.cpp430
-rw-r--r--src/mbgl/renderer/painter.hpp186
-rw-r--r--src/mbgl/renderer/painter_background.cpp84
-rw-r--r--src/mbgl/renderer/painter_circle.cpp58
-rw-r--r--src/mbgl/renderer/painter_clipping.cpp38
-rw-r--r--src/mbgl/renderer/painter_debug.cpp135
-rw-r--r--src/mbgl/renderer/painter_fill.cpp144
-rw-r--r--src/mbgl/renderer/painter_fill_extrusion.cpp89
-rw-r--r--src/mbgl/renderer/painter_line.cpp92
-rw-r--r--src/mbgl/renderer/painter_raster.cpp89
-rw-r--r--src/mbgl/renderer/painter_symbol.cpp166
-rw-r--r--src/mbgl/renderer/possibly_evaluated_property_value.hpp14
-rw-r--r--src/mbgl/renderer/property_evaluation_parameters.hpp10
-rw-r--r--src/mbgl/renderer/raster_bucket.cpp30
-rw-r--r--src/mbgl/renderer/raster_bucket.hpp22
-rw-r--r--src/mbgl/renderer/render_background_layer.cpp37
-rw-r--r--src/mbgl/renderer/render_circle_layer.cpp67
-rw-r--r--src/mbgl/renderer/render_custom_layer.cpp29
-rw-r--r--src/mbgl/renderer/render_fill_extrusion_layer.cpp55
-rw-r--r--src/mbgl/renderer/render_fill_layer.cpp71
-rw-r--r--src/mbgl/renderer/render_item.hpp36
-rw-r--r--src/mbgl/renderer/render_layer.cpp60
-rw-r--r--src/mbgl/renderer/render_layer.hpp30
-rw-r--r--src/mbgl/renderer/render_light.cpp20
-rw-r--r--src/mbgl/renderer/render_light.hpp85
-rw-r--r--src/mbgl/renderer/render_line_layer.cpp117
-rw-r--r--src/mbgl/renderer/render_pass.hpp8
-rw-r--r--src/mbgl/renderer/render_raster_layer.cpp35
-rw-r--r--src/mbgl/renderer/render_source.cpp36
-rw-r--r--src/mbgl/renderer/render_source.hpp59
-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_symbol_layer.cpp113
-rw-r--r--src/mbgl/renderer/render_tile.cpp129
-rw-r--r--src/mbgl/renderer/render_tile.hpp15
-rw-r--r--src/mbgl/renderer/renderer.cpp86
-rw-r--r--src/mbgl/renderer/renderer_backend.cpp69
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp742
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp111
-rw-r--r--src/mbgl/renderer/renderer_observer.hpp35
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp96
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.hpp41
-rw-r--r--src/mbgl/renderer/sources/render_image_source.cpp214
-rw-r--r--src/mbgl/renderer/sources/render_image_source.hpp58
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.cpp78
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.hpp39
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.cpp79
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.hpp39
-rw-r--r--src/mbgl/renderer/style_diff.cpp79
-rw-r--r--src/mbgl/renderer/style_diff.hpp48
-rw-r--r--src/mbgl/renderer/tile_mask.hpp15
-rw-r--r--src/mbgl/renderer/tile_parameters.hpp33
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp173
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp42
-rw-r--r--src/mbgl/renderer/transition_parameters.hpp (renamed from src/mbgl/renderer/cascade_parameters.hpp)4
-rw-r--r--src/mbgl/renderer/transitioning_property.hpp75
-rw-r--r--src/mbgl/renderer/update_parameters.hpp33
-rw-r--r--src/mbgl/shaders/circle.cpp60
-rw-r--r--src/mbgl/shaders/collision_box.cpp36
-rw-r--r--src/mbgl/shaders/debug.cpp7
-rw-r--r--src/mbgl/shaders/fill.cpp12
-rw-r--r--src/mbgl/shaders/fill_extrusion.cpp16
-rw-r--r--src/mbgl/shaders/fill_extrusion_pattern.cpp17
-rw-r--r--src/mbgl/shaders/fill_outline.cpp14
-rw-r--r--src/mbgl/shaders/fill_outline_pattern.cpp15
-rw-r--r--src/mbgl/shaders/fill_pattern.cpp13
-rw-r--r--src/mbgl/shaders/line.cpp40
-rw-r--r--src/mbgl/shaders/line_pattern.cpp41
-rw-r--r--src/mbgl/shaders/line_sdf.cpp87
-rw-r--r--src/mbgl/shaders/preludes.cpp23
-rw-r--r--src/mbgl/shaders/raster.cpp13
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp95
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp174
-rw-r--r--src/mbgl/sprite/sprite_atlas.cpp340
-rw-r--r--src/mbgl/sprite/sprite_atlas.hpp140
-rw-r--r--src/mbgl/sprite/sprite_atlas_observer.hpp15
-rw-r--r--src/mbgl/sprite/sprite_loader.cpp104
-rw-r--r--src/mbgl/sprite/sprite_loader.hpp44
-rw-r--r--src/mbgl/sprite/sprite_loader_observer.hpp21
-rw-r--r--src/mbgl/sprite/sprite_loader_worker.cpp (renamed from src/mbgl/sprite/sprite_atlas_worker.cpp)12
-rw-r--r--src/mbgl/sprite/sprite_loader_worker.hpp (renamed from src/mbgl/sprite/sprite_atlas_worker.hpp)8
-rw-r--r--src/mbgl/sprite/sprite_parser.cpp25
-rw-r--r--src/mbgl/sprite/sprite_parser.hpp24
-rw-r--r--src/mbgl/storage/asset_file_source.hpp3
-rw-r--r--src/mbgl/storage/file_source_request.cpp37
-rw-r--r--src/mbgl/storage/file_source_request.hpp31
-rw-r--r--src/mbgl/storage/local_file_source.hpp3
-rw-r--r--src/mbgl/storage/resource.cpp7
-rw-r--r--src/mbgl/storage/resource_transform.cpp13
-rw-r--r--src/mbgl/storage/response.cpp1
-rw-r--r--src/mbgl/style/class_dictionary.cpp51
-rw-r--r--src/mbgl/style/class_dictionary.hpp52
-rw-r--r--src/mbgl/style/collection.hpp141
-rw-r--r--src/mbgl/style/conversion/stringify.hpp9
-rw-r--r--src/mbgl/style/function/categorical_stops.cpp3
-rw-r--r--src/mbgl/style/function/identity_stops.cpp36
-rw-r--r--src/mbgl/style/image.cpp36
-rw-r--r--src/mbgl/style/image_impl.cpp24
-rw-r--r--src/mbgl/style/image_impl.hpp33
-rw-r--r--src/mbgl/style/layer.cpp30
-rw-r--r--src/mbgl/style/layer_impl.cpp14
-rw-r--r--src/mbgl/style/layer_impl.hpp28
-rw-r--r--src/mbgl/style/layer_observer.hpp6
-rw-r--r--src/mbgl/style/layers/background_layer.cpp126
-rw-r--r--src/mbgl/style/layers/background_layer_impl.cpp5
-rw-r--r--src/mbgl/style/layers/background_layer_impl.hpp9
-rw-r--r--src/mbgl/style/layers/background_layer_properties.hpp4
-rw-r--r--src/mbgl/style/layers/circle_layer.cpp366
-rw-r--r--src/mbgl/style/layers/circle_layer_impl.cpp9
-rw-r--r--src/mbgl/style/layers/circle_layer_impl.hpp9
-rw-r--r--src/mbgl/style/layers/circle_layer_properties.hpp8
-rw-r--r--src/mbgl/style/layers/custom_layer.cpp56
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp60
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp15
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer.cpp251
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer_impl.cpp9
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer_impl.hpp9
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer_properties.hpp3
-rw-r--r--src/mbgl/style/layers/fill_layer.cpp251
-rw-r--r--src/mbgl/style/layers/fill_layer_impl.cpp9
-rw-r--r--src/mbgl/style/layers/fill_layer_impl.hpp9
-rw-r--r--src/mbgl/style/layers/fill_layer_properties.hpp3
-rw-r--r--src/mbgl/style/layers/layer.cpp.ejs117
-rw-r--r--src/mbgl/style/layers/layer_properties.hpp.ejs6
-rw-r--r--src/mbgl/style/layers/line_layer.cpp373
-rw-r--r--src/mbgl/style/layers/line_layer_impl.cpp10
-rw-r--r--src/mbgl/style/layers/line_layer_impl.hpp11
-rw-r--r--src/mbgl/style/layers/line_layer_properties.hpp9
-rw-r--r--src/mbgl/style/layers/raster_layer.cpp225
-rw-r--r--src/mbgl/style/layers/raster_layer_impl.cpp5
-rw-r--r--src/mbgl/style/layers/raster_layer_impl.hpp9
-rw-r--r--src/mbgl/style/layers/raster_layer_properties.hpp4
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp777
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.cpp10
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.hpp13
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp27
-rw-r--r--src/mbgl/style/layout_property.hpp95
-rw-r--r--src/mbgl/style/light.cpp79
-rw-r--r--src/mbgl/style/light.cpp.ejs31
-rw-r--r--src/mbgl/style/light_impl.cpp4
-rw-r--r--src/mbgl/style/light_impl.hpp53
-rw-r--r--src/mbgl/style/light_observer.hpp4
-rw-r--r--src/mbgl/style/light_properties.hpp51
-rw-r--r--src/mbgl/style/observer.hpp12
-rw-r--r--src/mbgl/style/paint_property.hpp161
-rw-r--r--src/mbgl/style/parser.cpp29
-rw-r--r--src/mbgl/style/parser.hpp2
-rw-r--r--src/mbgl/style/properties.hpp248
-rw-r--r--src/mbgl/style/rapidjson_conversion.hpp7
-rw-r--r--src/mbgl/style/source.cpp24
-rw-r--r--src/mbgl/style/source_impl.cpp19
-rw-r--r--src/mbgl/style/source_impl.hpp21
-rw-r--r--src/mbgl/style/sources/geojson_source.cpp70
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.cpp92
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.hpp16
-rw-r--r--src/mbgl/style/sources/image_source.cpp84
-rw-r--r--src/mbgl/style/sources/image_source_impl.cpp38
-rw-r--r--src/mbgl/style/sources/image_source_impl.hpp30
-rw-r--r--src/mbgl/style/sources/raster_source.cpp74
-rw-r--r--src/mbgl/style/sources/raster_source_impl.cpp29
-rw-r--r--src/mbgl/style/sources/raster_source_impl.hpp16
-rw-r--r--src/mbgl/style/sources/vector_source.cpp71
-rw-r--r--src/mbgl/style/sources/vector_source_impl.cpp22
-rw-r--r--src/mbgl/style/sources/vector_source_impl.hpp14
-rw-r--r--src/mbgl/style/style.cpp820
-rw-r--r--src/mbgl/style/style.hpp192
-rw-r--r--src/mbgl/style/style_impl.cpp364
-rw-r--r--src/mbgl/style/style_impl.hpp144
-rw-r--r--src/mbgl/style/tile_source_impl.cpp78
-rw-r--r--src/mbgl/style/tile_source_impl.hpp47
-rw-r--r--src/mbgl/style/types.cpp21
-rw-r--r--src/mbgl/style/update_batch.hpp15
-rw-r--r--src/mbgl/text/collision_feature.cpp83
-rw-r--r--src/mbgl/text/collision_feature.hpp11
-rw-r--r--src/mbgl/text/collision_tile.cpp100
-rw-r--r--src/mbgl/text/collision_tile.hpp16
-rw-r--r--src/mbgl/text/get_anchors.cpp2
-rw-r--r--src/mbgl/text/glyph.hpp56
-rw-r--r--src/mbgl/text/glyph_atlas.cpp285
-rw-r--r--src/mbgl/text/glyph_atlas.hpp107
-rw-r--r--src/mbgl/text/glyph_manager.cpp145
-rw-r--r--src/mbgl/text/glyph_manager.hpp68
-rw-r--r--src/mbgl/text/glyph_manager_observer.hpp (renamed from src/mbgl/text/glyph_atlas_observer.hpp)4
-rw-r--r--src/mbgl/text/glyph_pbf.cpp10
-rw-r--r--src/mbgl/text/glyph_pbf.hpp18
-rw-r--r--src/mbgl/text/glyph_range.hpp4
-rw-r--r--src/mbgl/text/placement_config.hpp14
-rw-r--r--src/mbgl/text/quads.cpp348
-rw-r--r--src/mbgl/text/quads.hpp37
-rw-r--r--src/mbgl/text/shaping.cpp130
-rw-r--r--src/mbgl/text/shaping.hpp26
-rw-r--r--src/mbgl/tile/geojson_tile.cpp51
-rw-r--r--src/mbgl/tile/geometry_tile.cpp165
-rw-r--r--src/mbgl/tile/geometry_tile.hpp75
-rw-r--r--src/mbgl/tile/geometry_tile_data.hpp9
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp130
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp29
-rw-r--r--src/mbgl/tile/raster_tile.cpp22
-rw-r--r--src/mbgl/tile/raster_tile.hpp11
-rw-r--r--src/mbgl/tile/raster_tile_worker.cpp4
-rw-r--r--src/mbgl/tile/tile.cpp5
-rw-r--r--src/mbgl/tile/tile.hpp18
-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_impl.hpp3
-rw-r--r--src/mbgl/tile/vector_tile.cpp299
-rw-r--r--src/mbgl/tile/vector_tile_data.cpp89
-rw-r--r--src/mbgl/tile/vector_tile_data.hpp54
-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/http_header.cpp2
-rw-r--r--src/mbgl/util/i18n.cpp18
-rw-r--r--src/mbgl/util/intersection_tests.cpp2
-rw-r--r--src/mbgl/util/io.cpp6
-rw-r--r--src/mbgl/util/logging.cpp2
-rw-r--r--src/mbgl/util/longest_common_subsequence.hpp106
-rw-r--r--src/mbgl/util/mat2.hpp2
-rw-r--r--src/mbgl/util/mat4.cpp9
-rw-r--r--src/mbgl/util/math.hpp17
-rw-r--r--src/mbgl/util/offscreen_texture.cpp33
-rw-r--r--src/mbgl/util/offscreen_texture.hpp16
-rw-r--r--src/mbgl/util/premultiply.cpp2
-rw-r--r--src/mbgl/util/thread.hpp173
-rw-r--r--src/mbgl/util/thread_context.cpp13
-rw-r--r--src/mbgl/util/thread_context.hpp22
-rw-r--r--src/mbgl/util/thread_local.hpp44
-rw-r--r--src/mbgl/util/tile_cover.cpp21
-rw-r--r--src/mbgl/util/tile_cover.hpp3
-rw-r--r--src/mbgl/util/work_queue.cpp38
-rw-r--r--src/mbgl/util/work_queue.hpp40
-rw-r--r--test/.clang-tidy2
-rw-r--r--test/actor/actor.test.cpp223
-rw-r--r--test/actor/actor_ref.test.cpp80
-rw-r--r--test/algorithm/generate_clip_ids.test.cpp422
-rw-r--r--test/algorithm/update_renderables.test.cpp988
-rw-r--r--test/algorithm/update_tile_masks.test.cpp132
-rw-r--r--test/api/annotations.test.cpp172
-rw-r--r--test/api/api_misuse.test.cpp44
-rw-r--r--test/api/custom_layer.test.cpp22
-rw-r--r--test/api/query.test.cpp52
-rw-r--r--test/api/recycle_map.cpp58
-rw-r--r--test/api/render_missing.test.cpp64
-rw-r--r--test/api/repeated_render.test.cpp75
-rw-r--r--test/api/zoom_history.cpp71
-rw-r--r--test/fixtures/annotations/readd_image/expected.pngbin0 -> 1031 bytes
-rw-r--r--test/fixtures/api/empty-zoomed.json6
-rw-r--r--test/fixtures/image_manager/basic/expected.pngbin0 -> 646 bytes
-rw-r--r--test/fixtures/image_manager/updates_after/expected.pngbin0 -> 136 bytes
-rw-r--r--test/fixtures/image_manager/updates_before/expected.pngbin0 -> 123 bytes
-rw-r--r--test/fixtures/map/nocontent/expected.pngbin0 -> 1685 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_download/radar.gifbin0 -> 10958 bytes
-rw-r--r--test/fixtures/offline_download/style.json10
-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/sprite_atlas/basic/expected.pngbin694 -> 0 bytes
-rw-r--r--test/fixtures/sprite_atlas/size/expected.pngbin1118 -> 0 bytes
-rw-r--r--test/fixtures/sprite_atlas/updates_after/expected.pngbin135 -> 0 bytes
-rw-r--r--test/fixtures/sprite_atlas/updates_before/expected.pngbin110 -> 0 bytes
-rw-r--r--test/fixtures/style_parser/center-not-latlong.info.json7
-rw-r--r--test/fixtures/style_parser/center-not-latlong.style.json4
-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/style_parser/image-coordinates.info.json7
-rw-r--r--test/fixtures/style_parser/image-coordinates.style.json14
-rw-r--r--test/fixtures/style_parser/image-url.info.json7
-rw-r--r--test/fixtures/style_parser/image-url.style.json9
-rw-r--r--test/fixtures/zoom_history/expected.pngbin0 -> 3894 bytes
-rw-r--r--test/geometry/binpack.test.cpp51
-rw-r--r--test/gl/bucket.test.cpp251
-rw-r--r--test/gl/object.test.cpp7
-rw-r--r--test/map/map.test.cpp523
-rw-r--r--test/map/prefetch.test.cpp89
-rw-r--r--test/map/transform.test.cpp21
-rw-r--r--test/programs/symbol_program.test.cpp57
-rw-r--r--test/renderer/backend_scope.test.cpp30
-rw-r--r--test/renderer/group_by_layout.test.cpp2
-rw-r--r--test/renderer/image_manager.test.cpp150
-rw-r--r--test/sprite/sprite_atlas.test.cpp373
-rw-r--r--test/sprite/sprite_loader.test.cpp179
-rw-r--r--test/sprite/sprite_parser.test.cpp88
-rw-r--r--test/src/mbgl/test/conversion_stubs.hpp9
-rw-r--r--test/src/mbgl/test/fake_file_source.hpp4
-rw-r--r--test/src/mbgl/test/fixture_log_observer.cpp9
-rw-r--r--test/src/mbgl/test/fixture_log_observer.hpp10
-rw-r--r--test/src/mbgl/test/getrss.cpp4
-rw-r--r--test/src/mbgl/test/getrss.hpp4
-rw-r--r--test/src/mbgl/test/stub_file_source.cpp3
-rw-r--r--test/src/mbgl/test/stub_file_source.hpp1
-rw-r--r--test/src/mbgl/test/stub_geometry_tile_feature.hpp11
-rw-r--r--test/src/mbgl/test/stub_layer_observer.hpp26
-rw-r--r--test/src/mbgl/test/stub_style_observer.hpp20
-rw-r--r--test/src/mbgl/test/util.cpp23
-rw-r--r--test/src/mbgl/test/util.hpp9
-rw-r--r--test/storage/asset_file_source.test.cpp19
-rw-r--r--test/storage/default_file_source.test.cpp213
-rw-r--r--test/storage/http_file_source.test.cpp9
-rw-r--r--test/storage/local_file_source.test.cpp2
-rw-r--r--test/storage/offline.test.cpp8
-rw-r--r--test/storage/offline_database.test.cpp64
-rw-r--r--test/storage/offline_download.test.cpp11
-rw-r--r--test/storage/online_file_source.test.cpp10
-rw-r--r--test/storage/resource.test.cpp7
-rwxr-xr-xtest/storage/server.js6
-rw-r--r--test/storage/sqlite.test.cpp11
-rw-r--r--test/style/conversion/function.test.cpp14
-rw-r--r--test/style/conversion/layer.test.cpp18
-rw-r--r--test/style/conversion/light.test.cpp14
-rw-r--r--test/style/conversion/stringify.test.cpp17
-rw-r--r--test/style/filter.test.cpp8
-rw-r--r--test/style/function/exponential_stops.test.cpp20
-rw-r--r--test/style/function/interval_stops.test.cpp20
-rw-r--r--test/style/function/source_function.test.cpp9
-rw-r--r--test/style/properties.test.cpp (renamed from test/style/paint_property.test.cpp)43
-rw-r--r--test/style/source.test.cpp231
-rw-r--r--test/style/style.test.cpp42
-rw-r--r--test/style/style_image.test.cpp26
-rw-r--r--test/style/style_layer.test.cpp47
-rw-r--r--test/style/style_parser.test.cpp4
-rw-r--r--test/text/glyph_loader.test.cpp (renamed from test/text/glyph_atlas.test.cpp)67
-rw-r--r--test/text/quads.test.cpp276
-rw-r--r--test/tile/annotation_tile.test.cpp42
-rw-r--r--test/tile/geojson_tile.test.cpp28
-rw-r--r--test/tile/raster_tile.test.cpp18
-rw-r--r--test/tile/tile_id.test.cpp146
-rw-r--r--test/tile/vector_tile.test.cpp24
-rw-r--r--test/util/async_task.test.cpp59
-rw-r--r--test/util/dtoa.test.cpp24
-rw-r--r--test/util/image.test.cpp52
-rw-r--r--test/util/memory.test.cpp71
-rw-r--r--test/util/merge_lines.test.cpp113
-rw-r--r--test/util/offscreen_texture.test.cpp35
-rw-r--r--test/util/thread.test.cpp273
-rw-r--r--test/util/thread_local.test.cpp73
-rw-r--r--test/util/tile_cover.test.cpp9
-rw-r--r--test/util/work_queue.test.cpp59
1097 files changed, 39676 insertions, 31759 deletions
diff --git a/.clang-tidy b/.clang-tidy
index 6d080b2183..356748ec11 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,2 +1,2 @@
-Checks: 'modernize-*,misc-static-assert,llvm-namespace-comment,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-core.uninitialized.UndefReturn,-clang-analyzer-core.StackAddressEscape,-clang-analyzer-core.CallAndMessage,-clang-diagnostic-unused-command-line-argument,-clang-analyzer-core.uninitialized.*'
+Checks: 'modernize-*,misc-static-assert,llvm-namespace-comment,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-core.uninitialized.UndefReturn,-clang-analyzer-core.StackAddressEscape,-clang-analyzer-core.CallAndMessage,-clang-diagnostic-unused-command-line-argument,-clang-analyzer-core.uninitialized.*,-clang-analyzer-core.NullDereference,-clang-analyzer-core.NonNullParamChecker'
HeaderFilterRegex: '\/mbgl\/'
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/.travis.yml b/.travis.yml
deleted file mode 100644
index ac563289fc..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,240 +0,0 @@
-group: deprecated-2017Q2
-
-git:
- submodules: false
-
-# Save common build configurations as shortcuts, so we can reference them later.
-addons:
- apt:
- sources:
- - &common_sources [ 'ubuntu-toolchain-r-test', 'george-edison55-precise-backports', 'llvm-toolchain-trusty-3.9' ]
- packages:
- - &common_packages [ 'libllvm3.8v4', 'cmake', 'cmake-data' ]
- - &clang39_packages [ 'clang-3.9', 'libstdc++-5-dev', 'libstdc++6' ]
- - &gcc5_packages [ 'gcc-5', 'g++-5' ]
- - &glfw_packages [ 'libxrandr-dev', 'libxcursor-dev', 'libxinerama-dev' ]
-
-addons_shortcuts:
- addons_clang39: &clang39
- apt:
- sources: *common_sources
- packages:
- - *common_packages
- - *clang39_packages
- - *glfw_packages
- addons_gcc5: &gcc5
- apt:
- sources: *common_sources
- packages:
- - *common_packages
- - *gcc5_packages
- - *glfw_packages
- addons_qt4: &qt4
- apt:
- sources: *common_sources
- packages:
- - *common_packages
- - *gcc5_packages
- - [ 'libjemalloc-dev', 'mesa-utils', 'qt4-default', 'libqt4-sql-mysql' ]
- addons_qt5: &qt5
- apt:
- sources: *common_sources
- packages:
- - *common_packages
- - *gcc5_packages
- - [ 'mesa-utils', 'libc6-dbg', 'qt5-default', 'libqt5opengl5-dev', 'qtdeclarative5-dev', 'qtpositioning5-dev', 'qtlocation5-dev', 'libqt5sql5-sqlite' ]
-
-env:
- global:
- - TERM: dumb
- - CCACHE: 1
- - CCACHE_MAXSIZE: 384M
- # AWS
- - secure: "MZHblLZXG/jWf2w0ZFlxCLDwx2qtGgRDODQyg1BR7JIuMz6AtWv8XR/sUczWLbiABCL0a/NzJF1g4v2pI7X69IntcjOdIABBgTh7++6+1TJ0Kp8viEltb55nQG3lHy/R6fOaI7Pj9tuCX0PCRtGA5C/fGnodLGEjy3RVOJ09ln0="
- - secure: "KaSQbhgjtV7ZCkesHmvrNsbQVjk5SPfGKB1VkWenRGYhLF45HpSRNwSxMQddZ566Pg7qIFgF1iWl/B0QW3B6AWL5WmzQ5AOJgwS876pNIc/UT7ubMPtgAtjpvw1bQvQP3B8MrB+3OE5c6tD+a3LhR9krV//dOsfErR5Yy+3Mbkc="
- # Access Token
- - secure: "RiBIBfVhhaMjU5ksuwJO3shdvG9FpinBjdSv4co9jg9171SR8edNriedHjVKSIeBhSGNmZmX+twS3dJS/By6tl/LKh9sTynA+ZAYYljkE7jn881B/gMrlYvdAA6og5KvkhV1/0iJWlhuZrMTkhpDR200iLgg3EWBhWjltzmDW/I="
-
-install:
- - source ./scripts/travis_helper.sh
- - source ./scripts/travis_setup.sh
- - ccache --zero-stats
-script:
- - make linux
- - make benchmark
- - make test
- - make run-test
-after_script:
- - ccache --show-stats
- - ./platform/linux/scripts/after_script.sh ${TRAVIS_JOB_NUMBER}
-after_success:
- - ./platform/linux/scripts/after_success.sh
-
-matrix:
- include:
- # LLVM 3.8.0 - clang-{format,tidy}
- - os: linux
- sudo: false
- dist: trusty
- language: cpp
- env: _CXX=c++ _CC=cc
- compiler: "check"
- script:
- - git fetch origin master:refs/remotes/origin/master
- - make check
-
- # EGL - Node v4 - Clang 3.9 - Release
- - os: linux
- sudo: required
- dist: trusty
- language: node
- compiler: "egl-node4-clang39-release"
- env: BUILDTYPE=Release _CXX=clang++-3.9 _CC=clang-3.9 WITH_EGL=1
- addons: *clang39
- before_script:
- # fglrx causes the GLX extension to be unavailable
- - sudo apt-get purge -qq fglrx
- - export PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)")
- - export PUBLISH=$([[ "${TRAVIS_TAG:-}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
- - mapbox_install_logbt
- - mapbox_start_xvfb
- - mapbox_export_mesa_library_path
- script:
- - nvm install 4
- - nvm use 4
- - make node
- - ./logbt -- $(scripts/mason.sh PREFIX apitrace VERSION 6a30de1)/bin/apitrace trace --api=egl -v make test-node
- after_script:
- - ccache --show-stats
- - ./platform/node/scripts/after_script.sh ${TRAVIS_JOB_NUMBER}
- after_success:
- - ./platform/node/scripts/after_success.sh
- after_failure:
- - aws s3 cp . s3://mapbox/mapbox-gl-native/render-tests/$TRAVIS_JOB_NUMBER --recursive --exclude "*" --include "*.trace"
-
- # EGL - Node v6 - Clang 3.9 - Debug
- - os: linux
- sudo: required
- dist: trusty
- language: node
- compiler: "egl-node6-clang39-debug"
- env: BUILDTYPE=Debug _CXX=clang++-3.9 _CC=clang-3.9 WITH_EGL=1
- addons: *clang39
- before_script:
- - export PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)")
- - export PUBLISH=$([[ "${TRAVIS_TAG:-}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
- - mapbox_install_logbt
- - mapbox_start_xvfb
- - mapbox_export_mesa_library_path
- script:
- - nvm install 6
- - nvm use 6
- - make node
- - ./logbt -- $(scripts/mason.sh PREFIX apitrace VERSION 6a30de1)/bin/apitrace trace --api=egl -v make test-node
- after_script:
- - ccache --show-stats
- - ./platform/node/scripts/after_script.sh ${TRAVIS_JOB_NUMBER}
- after_success:
- - ./platform/node/scripts/after_success.sh
- after_failure:
- - aws s3 cp . s3://mapbox/mapbox-gl-native/render-tests/$TRAVIS_JOB_NUMBER --recursive --exclude "*" --include "*.trace"
-
- # EGL - Node v6 - Clang 3.9 - Release
- - os: linux
- sudo: required
- dist: trusty
- language: node
- compiler: "egl-node6-clang39-release"
- env: BUILDTYPE=Release _CXX=clang++-3.9 _CC=clang-3.9 WITH_EGL=1
- addons: *clang39
- before_script:
- # fglrx causes the GLX extension to be unavailable
- - sudo apt-get purge -qq fglrx
- - export PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)")
- - export PUBLISH=$([[ "${TRAVIS_TAG:-}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
- - mapbox_install_logbt
- - mapbox_start_xvfb
- - mapbox_export_mesa_library_path
- script:
- - nvm install 6
- - nvm use 6
- - make node
- - ./logbt -- $(scripts/mason.sh PREFIX apitrace VERSION 6a30de1)/bin/apitrace trace --api=egl -v make test-node
- after_script:
- - ccache --show-stats
- - ./platform/node/scripts/after_script.sh ${TRAVIS_JOB_NUMBER}
- after_success:
- - ./platform/node/scripts/after_success.sh
- after_failure:
- - aws s3 cp . s3://mapbox/mapbox-gl-native/render-tests/$TRAVIS_JOB_NUMBER --recursive --exclude "*" --include "*.trace"
-
- # EGL - GCC 5 - Debug (Coverage)
- - os: linux
- sudo: required
- dist: trusty
- language: cpp
- compiler: "egl-gcc5-debug"
- env: BUILDTYPE=Debug _CXX=g++-5 _CC=gcc-5 WITH_COVERAGE=1 WITH_EGL=1
- addons: *gcc5
- before_script:
- - mapbox_start_xvfb
- - mapbox_export_mesa_library_path
- after_script:
- - ccache --show-stats
- - ./platform/linux/scripts/coveralls.sh
-
- # EGL - Clang 3.9 - Debug
- - os: linux
- sudo: required
- dist: trusty
- language: cpp
- compiler: "egl-clang39-debug"
- env: BUILDTYPE=Debug _CXX=clang++-3.9 _CC=clang-3.9 WITH_EGL=1
- addons: *clang39
- before_script:
- - mapbox_start_xvfb
- - mapbox_export_mesa_library_path
-
- # Qt 4 - GCC 5 - Release
- - os: linux
- sudo: required
- dist: trusty
- language: cpp
- compiler: "qt4-gcc5-release"
- env: BUILDTYPE=Release _CXX=g++-5 _CC=gcc-5
- addons: *qt4
- before_script:
- - mapbox_start_xvfb
- - mapbox_export_mesa_library_path
- script:
- - make qt-app
- - GTEST_OUTPUT=xml LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so make run-qt-test-Memory.*:*.Load
- - scripts/log_memory_benchmarks.sh test_detail.xml "Platform=Linux,Compiler=${_CC},Arch=$(uname -m)"
-
- # Qt 5 - GCC 5 - Release
- - os: linux
- sudo: required
- dist: trusty
- language: cpp
- compiler: "qt5-gcc5-release"
- env: BUILDTYPE=Release _CXX=g++-5 _CC=gcc-5 WITH_QT_I18N=1
- addons: *qt5
- before_script:
- - mapbox_start_xvfb
- - mapbox_export_mesa_library_path
- script:
- - make qt-app
- - make qt-test
- - make qt-docs
- - scripts/valgrind.sh build/qt-linux-x86_64/Release/mbgl-test --gtest_filter=-*.Load --gtest_filter=-Memory.Vector
-
-cache:
- directories:
- - $HOME/.ccache
- - $HOME/.cache/pip
- - node_modules
- - mason_packages/.binaries
-
-notifications:
- slack:
- secure: HHQYr7sF8M1SzoWSqgKVYtwAgGdLLCyTMsQjFhEEQNYO92ZwURE5s03qWTGH5k8+4Yqn26yrXt3NztLC4JIOpcGervN2mSZyq4dZgFTcWEd61igw0qwSenlwvFfbE1ASK/KYCzfyn9MIfHN+ovwLoRxXZkPwinKDvl3DXjBaFNg=
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index b3c44c62c9..639602020b 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -57,15 +57,49 @@ The "Style" component of mapbox-gl-native contains an implementation of the [Map
In addition to supporting styles loaded from a URL, mapbox-gl-native includes a runtime styling API, which allows users to dynamically modify the current style: add and remove layers, modify layer properties, and so on. As appropriate for a C++ API, the runtime styling API API is _strongly typed_: there are subclasses for each layer type, with correctly-typed accessors for each style property. This results in a large API surface area. Fortunately, this is automated, by generating the API – and the regular portion of the implementation – from the style specification.
-The layers API makes a distinction between public API and internal implementation using [the `Impl` idiom](https://github.com/mapbox/mapbox-gl-native/issues/3254) seen elsewhere in the codebase. Here, it takes the form of two parallel class hierarchies:
+The layers API makes a distinction between public API and internal implementation using [the `Impl` idiom](https://github.com/mapbox/mapbox-gl-native/issues/3254) seen elsewhere in the codebase. Here, it takes the form of parallel class hierarchies:
-* `Layer` and its subclasses form the public API.
-* `Layer::Impl` and its subclasses form the internal API.
+* `Layer`, `Source`, and their subclasses form the public API. This is the API consumed by SDK bindings.
+* `Layer::Impl`, `Source::Impl`, and their subclasses form the internal API. This API is used only by other parts of the core C++ implementation.
-As well as forming the boundary between public and internal, these two class hierarchies form the boundary between generated code and handwritten code. Except for `CustomLayer` and `CustomLayer::Impl`:
+For each subclass of `Layer` or source, there's a corresponding `Impl` subclass. For example, `CircleLayer` and `CircleLayer::Impl`. The base `Layer` class holds a reference to the base `Layer::Impl`, and `CircleLayer` and other subclasses have an `impl()` accessor method that casts this reference to the appropriate subtype.
-* `Layer` subclasses are entirely generated. (`Layer` itself is handwritten.)
-* `Layer::Impl` and its subclasses are entirely handwritten.
+## Immutability
+
+The `Layer::Impl` and `Source::Impl` reference held by `Layer` and `Source` base classes is _immutable_: it's a shared reference to a `const` (read only) pointer. Immutability permits the `Impl` objects to be shared safely between threads when needed -- for example, between the main thread and worker thread that performs computation in the background, or between the main thread and a dedicated renderer thread. See the "Threading" section below for further details on the threading model.
+
+Immutability is an alternative to several other strategies for safe intra-thread communication. One alternative strategy is to insert locks whenever data is shared between threads and at least one thread may be modifying the data. This strategy is prone to problems such as race conditions (if you forget to use a lock), deadlocks (if locks are acquired in conflicting orders), and poor performance due to lock contention. Another strategy is to copy data whenever it's needed by another thread. For complex structures such as a Mapbox Style, copying can be an expensive operation. With immutability, the approach is to share data without copying, but ensure that any data so shared cannot be modified by any thread, and that the data is not destroyed until the last thread using it relinquishes its reference.
+
+Immutability is implemented by the `Immutable<T>` template, which acts as a non-nullable shared reference to a `const T`. It has behavior similar to `std::shared_ptr<const T>`, but indicates its intent as an immutable reference that is safe to share between threads.
+
+Immutability is core to the implementation, and yet one of the defining features of Mapbox GL is the ability to manipulate and mutate the style freely at runtime. How can this be possible if everything is immutable? The answer turns on the distinction mentioned in the previous section between public classes such as `Layer` and private implementations such as `Layer::Impl`. In Mapbox GL, the latter is immutable, but the former is not:
+
+* **Mutable objects**: `Layer`, `Source`, `Image`, `Light`
+* **Immutable objects**: `Layer::Impl`, `Source::Impl`, `Image::Impl`, `Light::Impl`
+
+An instance of a `Layer` subclass such as `CircleLayer` has a reference to an `Immutable<Layer::Impl>`, but is itself mutable -- it has mutating methods such as `setCircleRadius`. Such methods follow a common pattern:
+
+* Create a new instance of the Impl, copied from the existing Impl. This new instance is temporarily mutable.
+* Modify this new instance as needed; e.g. set the radius to a new value.
+* "Freeze" the new instance by making it immutable, and then assign that immutable reference to be the new Impl for the `Layer`.
+
+Two things to note about this process:
+
+* No existing Impls are modified -- only the newly created copy.
+* Only one existing reference to an Impl is modified -- the one held by the `Layer` instance being mutated. Any references to the previous Impl held by other threads remain unchanged. They go on using the previous value for radius until notified by some other means that there has been a change.
+
+So how do things that are holding references to the previous Impl get notified? The answer is **style diffing**. This is the process by which we determine, from one frame to the next, what parts of the style have changed and therefore what needs to be recalculated in response to those changes in order to draw an updated frame. In parallel to style objects such as `Style`, `Layer`, `Source` and so on, Mapbox GL maintains render objects such as `RenderStyle`, `RenderLayer`, `RenderSource` and so on. These render objects:
+
+* are mutable
+* may live on either the main thread or an independent rendering thread, depending on the SDK
+* contain any and all values calculated from the style objects plus state such as the current zoom level and location -- for example, the set of loaded tiles and the buckets calculated from them
+
+From one frame to the next, these render objects are updated based on the current state of the style objects. In order to permit the render objects to live on a different thread, this state is communicated as immutable Impl references -- in effect, a snapshot of the style state at the time the frame was requested. And it really is a just snapshot -- we don't communicate things like "the radius of this circle layer was changed", "this layer was removed", etc. Instead, that information is reconstructed by `RenderStyle` by comparing the new snapshot to the old snapshot. Immutability allows us to perform this comparison very efficiently:
+
+* If the "before" and "after" of two immutable references refer to the same object, we know that it hasn't changed. If they're different, we know it has changed in some way.
+* We can calculate changes in collections of immutable references (a list of layers or sources) using an efficient [diff algorithm](http://www.xmailserver.org/diff2.pdf). We can further improve this efficiency by making the collections themselves immutable, so that we can avoid running the diff algorithm altogether in the common case where the "before" and "after" collections refer to the same immutable object.
+
+One final benefit of this approach of diffing immutable objects: we get "smart" updates between two completely independent styles essentially for free. For example, `RenderStyle` doesn't care if the "before" snapshot is from the Mapbox Light style and the "after" snapshot is from the Mapbox Dark style. It will just calculate the difference between the two snapshots, update render data where necessary, and render the next frame. Since those two styles use the same source data and layer IDs, the result will be a fully automatic, seamless transition between light and dark.
## FileSource
## Layout
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ac2ea7e0f..530461d1fa 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)
@@ -41,21 +42,24 @@ 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.2.1 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(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(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)
@@ -64,14 +68,16 @@ 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)
+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_CXX_COMPILER_ID MATCHES ".*Clang")
@@ -81,6 +87,42 @@ elseif(CMAKE_COMPILER_IS_GNUCXX)
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()
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..964ac01a88 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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..61d2271de5 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -11,40 +11,47 @@ 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:
diff --git a/Makefile b/Makefile
index b09d1a9ba3..b4863a9b9e 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)
@@ -310,7 +299,12 @@ benchmark: $(LINUX_BUILD)
$(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-benchmark
ifneq (,$(shell command -v gdb 2> /dev/null))
- GDB = 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
@@ -388,7 +382,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)
@@ -404,17 +398,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)
@@ -454,6 +440,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
@@ -474,21 +466,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
@@ -506,14 +484,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)
@@ -546,26 +527,30 @@ 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="$$*"
endef
@@ -577,39 +562,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
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 "$*"
-
+# 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
@@ -617,29 +602,53 @@ 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
+# 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
@@ -662,7 +671,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..8f6ea99704 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 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 Android SDK](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 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) |
+| [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 faf6c4fbe4..75695c1ad7 100644
--- a/benchmark/api/query.benchmark.cpp
+++ b/benchmark/api/query.benchmark.cpp
@@ -1,11 +1,10 @@
#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>
#include <mbgl/storage/network_status.hpp>
@@ -23,21 +22,19 @@ public:
NetworkStatus::Set(NetworkStatus::Status::Offline);
fileSource.setAccessToken("foobar");
- map.setStyleJSON(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.addImage("test-icon", std::make_unique<style::Image>(
+ 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::Still };
ScreenBox box{{ 0, 0 }, { 1000, 1000 }};
};
@@ -47,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, {});
}
}
@@ -55,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" }}, {}});
}
}
@@ -63,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..9f1ea68e0b
--- /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::Still };
+ 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::Still };
+
+ 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::Still };
+ 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..1f8fe4579f
--- /dev/null
+++ b/benchmark/function/camera_function.benchmark.cpp
@@ -0,0 +1,69 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/style/function/source_function.hpp>
+
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+#include <rapidjson/document.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
+
+ 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 += "]";
+
+ doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + "}");
+ return doc;
+}
+
+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::convert<CameraFunction<float>, JSValue>(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::convert<CameraFunction<float>, JSValue>(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..f04b6c7073
--- /dev/null
+++ b/benchmark/function/composite_function.benchmark.cpp
@@ -0,0 +1,76 @@
+#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/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+#include <rapidjson/document.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
+
+ 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 += "]";
+
+ doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})");
+ return doc;
+}
+
+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::convert<style::CompositeFunction<float>, JSValue>(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::convert<CompositeFunction<float>, JSValue>(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..14e729eee2
--- /dev/null
+++ b/benchmark/function/source_function.benchmark.cpp
@@ -0,0 +1,70 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/benchmark/stub_geometry_tile_feature.hpp>
+
+#include <mbgl/style/function/source_function.hpp>
+
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+#include <rapidjson/document.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
+
+ 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 += "]";
+
+ doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})");
+ return doc;
+}
+
+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::convert<SourceFunction<float>, JSValue>(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::convert<SourceFunction<float>, JSValue>(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/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/parse/vector_tile.benchmark.cpp b/benchmark/parse/vector_tile.benchmark.cpp
new file mode 100644
index 0000000000..24623dbda7
--- /dev/null
+++ b/benchmark/parse/vector_tile.benchmark.cpp
@@ -0,0 +1,28 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/tile/vector_tile_data.hpp>
+#include <mbgl/util/io.hpp>
+
+using namespace mbgl;
+
+static void Parse_VectorTile(benchmark::State& state) {
+ auto data = std::make_shared<std::string>(util::read_file("test/fixtures/api/assets/streets/10-163-395.vector.pbf"));
+
+ while (state.KeepRunning()) {
+ std::size_t length = 0;
+ VectorTileData tile(data);
+ for (const auto& name : tile.layerNames()) {
+ if (auto layer = tile.getLayer(name)) {
+ const std::size_t count = layer->featureCount();
+ for (std::size_t i = 0; i < count; i++) {
+ if (auto feature = layer->getFeature(i)) {
+ length += feature->getGeometries().size();
+ length += feature->getProperties().size();
+ }
+ }
+ }
+ }
+ }
+}
+
+BENCHMARK(Parse_VectorTile);
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/render.cpp b/bin/render.cpp
index 0af933475a..444921ff2b 100644
--- a/bin/render.cpp
+++ b/bin/render.cpp
@@ -1,12 +1,11 @@
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/run_loop.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"
@@ -27,14 +26,13 @@ int main(int argc, char *argv[]) {
double zoom = 0;
double bearing = 0;
double pitch = 0;
+ double pixelRatio = 1;
- uint32_t 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::vector<std::string> classes;
std::string token;
bool debug = false;
@@ -49,7 +47,6 @@ int main(int argc, char *argv[]) {
("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")
- ("class,c", po::value(&classes)->value_name("name"), "Class name")
("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")
@@ -84,20 +81,15 @@ int main(int argc, char *argv[]) {
fileSource.setAccessToken(std::string(token));
}
- HeadlessBackend backend;
- BackendScope scope { backend };
- OffscreenView view(backend.getContext(), { width * pixelRatio, 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::Still);
if (style_path.find("://") == std::string::npos) {
style_path = std::string("file://") + style_path;
}
- map.setStyleURL(style_path);
-
- map.setClasses(classes);
-
+ map.getStyle().loadURL(style_path);
map.setLatLngZoom({ lat, lon }, zoom);
map.setBearing(bearing);
map.setPitch(pitch);
@@ -106,23 +98,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 7d75427bcb..6ff8a84f87 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,31 +1,174 @@
version: 2
-jobs:
+workflows:
+ version: 2
+ default:
+ jobs:
+ - 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
+ - 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
+
+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 }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
+ - 'v3/{{ .Environment.CIRCLE_JOB }}/master/{{ checksum ".circle-week" }}'
+ - &save-cache
+ save_cache:
+ key: 'v3/{{ .Environment.CIRCLE_JOB }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
+ paths: [ "node_modules", "/root/.ccache", "mason_packages/.binaries" ]
+
+
+ - &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
+
+ - &run-node-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-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
+
+
+ - &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:
# ------------------------------------------------------------------------------
- build:
+ clang-tidy:
docker:
- - image: mbgl/ci:trigger_job
- working_directory: /
+ - image: mbgl/ci:r4-linux-clang-3.9
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: Debug
steps:
- - deploy:
- name: Trigger 'android-debug-arm-v7'
- command: trigger_job android-debug-arm-v7
- - deploy:
- name: Trigger 'android-release-all'
- command: trigger_job android-release-all
+ - checkout
+ - *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
# ------------------------------------------------------------------------------
android-debug-arm-v7:
docker:
- - image: mbgl/ci:r2-android-ndk-r13b-gradle
+ - image: mbgl/ci@sha256:c34e221294d81da80918d3e9a9df5de795b067e88f86d7c9a5e262763332536e
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Debug
+ IS_LOCAL_DEVELOPMENT: false
steps:
- checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
- run:
name: Build libmapbox-gl.so for arm-v7
command: make android-lib-arm-v7
@@ -36,19 +179,26 @@ 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
- run:
name: Log in to Google Cloud Platform
shell: /bin/bash -euo pipefail
@@ -58,14 +208,15 @@ 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=$?
+ --device-ids shamu --os-version-ids 22 --locales en --orientations portrait --timeout 20m \
+ --test-targets "package com.mapbox.mapboxsdk.testapp.style" 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}"
@@ -79,18 +230,35 @@ jobs:
- store_artifacts:
path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
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:r2-android-ndk-r13b-gradle
+ - image: mbgl/ci@sha256:c34e221294d81da80918d3e9a9df5de795b067e88f86d7c9a5e262763332536e
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Release
+ IS_LOCAL_DEVELOPMENT: false
steps:
- checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
- run:
name: Generate Maven credentials
shell: /bin/bash -euo pipefail
@@ -102,26 +270,10 @@ jobs:
signing.password=$SIGNING_PASSWORD
signing.secretKeyRingFile=secring.gpg" >> platform/android/MapboxGLAndroidSDK/gradle.properties
- run:
- name: Build libmapbox-gl.so for arm-v7
- command: make android-lib-arm-v7
- - run:
- name: Build libmapbox-gl.so for arm-v8
- command: make android-lib-arm-v8
- - run:
- name: Build libmapbox-gl.so for arm-v5
- command: make android-lib-arm-v5
- - run:
- name: Build libmapbox-gl.so for mips
- command: make android-lib-mips
- - run:
- name: Build libmapbox-gl.so for x86
- command: make android-lib-x86
- - run:
- name: Build libmapbox-gl.so for x86-64
- command: make android-lib-x86-64
- - run:
name: Build package
command: make apackage
+ - *show-ccache-stats
+ - *save-cache
- store_artifacts:
path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
destination: .
@@ -134,3 +286,300 @@ jobs:
name: Publish to Maven
command: |
if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi
+
+
+# ------------------------------------------------------------------------------
+ node4-clang39-release:
+ docker:
+ - image: mbgl/ci:r4-linux-clang-3.9-node-4
+ working_directory: /src
+ environment:
+ 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)
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-tests
+ - *publish-node-package
+ - *upload-render-tests
+
+# ------------------------------------------------------------------------------
+ node6-clang39-release:
+ docker:
+ - image: mbgl/ci:r4-linux-clang-3.9
+ working_directory: /src
+ environment:
+ 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)
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-tests
+ - *publish-node-package
+ - *upload-render-tests
+
+# ------------------------------------------------------------------------------
+ node6-clang39-debug:
+ docker:
+ - image: mbgl/ci:r4-linux-clang-3.9
+ working_directory: /src
+ environment:
+ 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)
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-tests-recycle-map
+ - *publish-node-package
+ - *upload-render-tests-recycle-map
+
+# ------------------------------------------------------------------------------
+ linux-clang39-debug:
+ docker:
+ - image: mbgl/ci:r4-linux-clang-3.9
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: Debug
+ WITH_EGL: 1
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-linux
+ - *build-benchmark
+ - *build-test
+ - *show-ccache-stats
+ - *save-cache
+ - *run-unit-tests
+
+# ------------------------------------------------------------------------------
+ linux-clang4-sanitize-address:
+ docker:
+ - image: mbgl/ci:r4-linux-clang-4
+ 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: 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/ci:r4-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: 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/ci:r4-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: |
+ 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/ci:r4-linux-gcc-4.9
+ resource_class: large
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 2
+ 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:r4-linux-gcc-5
+ resource_class: large
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 2
+ BUILDTYPE: Debug
+ WITH_EGL: 1
+ WITH_COVERAGE: 1
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-linux
+ - *build-benchmark
+ - *build-test
+ - *show-ccache-stats
+ - *save-cache
+ - *run-unit-tests
+ - run:
+ name: Upload coverage results to coveralls
+ command: |
+ platform/linux/scripts/coveralls.sh
+
+# ------------------------------------------------------------------------------
+ linux-gcc5-release-qt4:
+ docker:
+ - image: mbgl/ci:r4-linux-gcc-5-qt-4
+ resource_class: large
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 2 # OOM, causing the compiler to crash.
+ BUILDTYPE: Release
+ GTEST_OUTPUT: xml
+ LD_PRELOAD: /usr/lib/x86_64-linux-gnu/libjemalloc.so
+ steps:
+ - checkout
+ - *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: |
+ 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)"
+
+# ------------------------------------------------------------------------------
+ linux-gcc5-release-qt5:
+ docker:
+ - image: mbgl/ci:r4-linux-gcc-5-qt-5
+ resource_class: large
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 2 # OOM, causing the compiler to crash.
+ BUILDTYPE: Release
+ WITH_QT_I18N: 1
+ steps:
+ - checkout
+ - *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: |
+ 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
diff --git a/cloudformation/travis.template b/cloudformation/travis.template
index 29f2402b20..db4e59eb7e 100644
--- a/cloudformation/travis.template
+++ b/cloudformation/travis.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/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake
index 0306340fe0..9161209128 100644
--- a/cmake/benchmark-files.cmake
+++ b/cmake/benchmark-files.cmake
@@ -3,18 +3,28 @@
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
benchmark/src/main.cpp
# 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 c298d8ee28..87351e97b1 100644
--- a/cmake/benchmark.cmake
+++ b/cmake/benchmark.cmake
@@ -17,9 +17,21 @@ 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 rapidjson)
+target_add_mason_package(mbgl-benchmark PRIVATE protozero)
+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 8a3c42a1f4..c34a0724f4 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -2,12 +2,13 @@
set(MBGL_CORE_FILES
# actor
+ include/mbgl/actor/actor.hpp
+ include/mbgl/actor/actor_ref.hpp
include/mbgl/actor/mailbox.hpp
+ include/mbgl/actor/message.hpp
include/mbgl/actor/scheduler.hpp
- src/mbgl/actor/actor.hpp
- src/mbgl/actor/actor_ref.hpp
src/mbgl/actor/mailbox.cpp
- src/mbgl/actor/message.hpp
+ 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
@@ -32,8 +34,6 @@ set(MBGL_CORE_FILES
src/mbgl/annotation/render_annotation_source.hpp
src/mbgl/annotation/shape_annotation_impl.cpp
src/mbgl/annotation/shape_annotation_impl.hpp
- src/mbgl/annotation/style_sourced_annotation_impl.cpp
- src/mbgl/annotation/style_sourced_annotation_impl.hpp
src/mbgl/annotation/symbol_annotation_impl.cpp
src/mbgl/annotation/symbol_annotation_impl.hpp
@@ -43,7 +43,6 @@ set(MBGL_CORE_FILES
# geometry
src/mbgl/geometry/anchor.hpp
- src/mbgl/geometry/binpack.hpp
src/mbgl/geometry/debug_font_data.hpp
src/mbgl/geometry/feature_index.cpp
src/mbgl/geometry/feature_index.hpp
@@ -100,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
@@ -155,91 +149,104 @@ 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.cpp
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/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/cascade_parameters.hpp
- src/mbgl/renderer/circle_bucket.cpp
- src/mbgl/renderer/circle_bucket.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/debug_bucket.cpp
- src/mbgl/renderer/debug_bucket.hpp
- src/mbgl/renderer/fill_bucket.cpp
- src/mbgl/renderer/fill_bucket.hpp
- src/mbgl/renderer/fill_extrusion_bucket.cpp
- src/mbgl/renderer/fill_extrusion_bucket.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/line_bucket.cpp
- src/mbgl/renderer/line_bucket.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/painter_background.cpp
- src/mbgl/renderer/painter_circle.cpp
- src/mbgl/renderer/painter_clipping.cpp
- src/mbgl/renderer/painter_debug.cpp
- src/mbgl/renderer/painter_fill.cpp
- src/mbgl/renderer/painter_fill_extrusion.cpp
- src/mbgl/renderer/painter_line.cpp
- src/mbgl/renderer/painter_raster.cpp
- src/mbgl/renderer/painter_symbol.cpp
src/mbgl/renderer/possibly_evaluated_property_value.hpp
src/mbgl/renderer/property_evaluation_parameters.hpp
src/mbgl/renderer/property_evaluator.hpp
- src/mbgl/renderer/raster_bucket.cpp
- src/mbgl/renderer/raster_bucket.hpp
- src/mbgl/renderer/render_background_layer.cpp
- src/mbgl/renderer/render_background_layer.hpp
- src/mbgl/renderer/render_circle_layer.cpp
- src/mbgl/renderer/render_circle_layer.hpp
- src/mbgl/renderer/render_custom_layer.cpp
- src/mbgl/renderer/render_custom_layer.hpp
- src/mbgl/renderer/render_fill_extrusion_layer.cpp
- src/mbgl/renderer/render_fill_extrusion_layer.hpp
- src/mbgl/renderer/render_fill_layer.cpp
- src/mbgl/renderer/render_fill_layer.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
src/mbgl/renderer/render_light.hpp
- src/mbgl/renderer/render_line_layer.cpp
- src/mbgl/renderer/render_line_layer.hpp
src/mbgl/renderer/render_pass.hpp
- src/mbgl/renderer/render_raster_layer.cpp
- src/mbgl/renderer/render_raster_layer.hpp
src/mbgl/renderer/render_source.cpp
src/mbgl/renderer/render_source.hpp
src/mbgl/renderer/render_source_observer.hpp
- src/mbgl/renderer/render_symbol_layer.cpp
- src/mbgl/renderer/render_symbol_layer.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/symbol_bucket.cpp
- src/mbgl/renderer/symbol_bucket.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
- src/mbgl/renderer/transitioning_property.hpp
+ src/mbgl/renderer/transition_parameters.hpp
src/mbgl/renderer/update_parameters.hpp
+ # renderer/buckets
+ src/mbgl/renderer/buckets/circle_bucket.cpp
+ src/mbgl/renderer/buckets/circle_bucket.hpp
+ src/mbgl/renderer/buckets/debug_bucket.cpp
+ src/mbgl/renderer/buckets/debug_bucket.hpp
+ src/mbgl/renderer/buckets/fill_bucket.cpp
+ src/mbgl/renderer/buckets/fill_bucket.hpp
+ src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
+ src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
+ src/mbgl/renderer/buckets/line_bucket.cpp
+ src/mbgl/renderer/buckets/line_bucket.hpp
+ src/mbgl/renderer/buckets/raster_bucket.cpp
+ src/mbgl/renderer/buckets/raster_bucket.hpp
+ src/mbgl/renderer/buckets/symbol_bucket.cpp
+ src/mbgl/renderer/buckets/symbol_bucket.hpp
+
+ # renderer/layers
+ src/mbgl/renderer/layers/render_background_layer.cpp
+ src/mbgl/renderer/layers/render_background_layer.hpp
+ src/mbgl/renderer/layers/render_circle_layer.cpp
+ src/mbgl/renderer/layers/render_circle_layer.hpp
+ src/mbgl/renderer/layers/render_custom_layer.cpp
+ src/mbgl/renderer/layers/render_custom_layer.hpp
+ src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+ src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
+ src/mbgl/renderer/layers/render_fill_layer.cpp
+ src/mbgl/renderer/layers/render_fill_layer.hpp
+ src/mbgl/renderer/layers/render_line_layer.cpp
+ src/mbgl/renderer/layers/render_line_layer.hpp
+ src/mbgl/renderer/layers/render_raster_layer.cpp
+ src/mbgl/renderer/layers/render_raster_layer.hpp
+ src/mbgl/renderer/layers/render_symbol_layer.cpp
+ src/mbgl/renderer/layers/render_symbol_layer.hpp
+
# renderer/sources
src/mbgl/renderer/sources/render_geojson_source.cpp
src/mbgl/renderer/sources/render_geojson_source.hpp
+ src/mbgl/renderer/sources/render_image_source.cpp
+ src/mbgl/renderer/sources/render_image_source.hpp
src/mbgl/renderer/sources/render_raster_source.cpp
src/mbgl/renderer/sources/render_raster_source.hpp
src/mbgl/renderer/sources/render_vector_source.cpp
@@ -284,11 +291,11 @@ set(MBGL_CORE_FILES
src/mbgl/shaders/symbol_sdf.hpp
# sprite
- src/mbgl/sprite/sprite_atlas.cpp
- src/mbgl/sprite/sprite_atlas.hpp
- src/mbgl/sprite/sprite_atlas_observer.hpp
- src/mbgl/sprite/sprite_atlas_worker.cpp
- src/mbgl/sprite/sprite_atlas_worker.hpp
+ src/mbgl/sprite/sprite_loader.cpp
+ src/mbgl/sprite/sprite_loader.hpp
+ src/mbgl/sprite/sprite_loader_observer.hpp
+ src/mbgl/sprite/sprite_loader_worker.cpp
+ src/mbgl/sprite/sprite_loader_worker.hpp
src/mbgl/sprite/sprite_parser.cpp
src/mbgl/sprite/sprite_parser.hpp
@@ -299,12 +306,16 @@ set(MBGL_CORE_FILES
include/mbgl/storage/offline.hpp
include/mbgl/storage/online_file_source.hpp
include/mbgl/storage/resource.hpp
+ 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
src/mbgl/storage/resource.cpp
+ src/mbgl/storage/resource_transform.cpp
src/mbgl/storage/response.cpp
# style
@@ -319,12 +330,14 @@ set(MBGL_CORE_FILES
include/mbgl/style/position.hpp
include/mbgl/style/property_value.hpp
include/mbgl/style/source.hpp
+ include/mbgl/style/style.hpp
include/mbgl/style/transition_options.hpp
include/mbgl/style/types.hpp
include/mbgl/style/undefined.hpp
- src/mbgl/style/class_dictionary.cpp
- src/mbgl/style/class_dictionary.hpp
+ src/mbgl/style/collection.hpp
src/mbgl/style/image.cpp
+ src/mbgl/style/image_impl.cpp
+ src/mbgl/style/image_impl.hpp
src/mbgl/style/layer.cpp
src/mbgl/style/layer_impl.cpp
src/mbgl/style/layer_impl.hpp
@@ -334,25 +347,24 @@ set(MBGL_CORE_FILES
src/mbgl/style/light_impl.cpp
src/mbgl/style/light_impl.hpp
src/mbgl/style/light_observer.hpp
- src/mbgl/style/light_properties.hpp
src/mbgl/style/observer.hpp
src/mbgl/style/paint_property.hpp
src/mbgl/style/parser.cpp
src/mbgl/style/parser.hpp
+ src/mbgl/style/properties.hpp
src/mbgl/style/rapidjson_conversion.hpp
src/mbgl/style/source.cpp
src/mbgl/style/source_impl.cpp
src/mbgl/style/source_impl.hpp
src/mbgl/style/source_observer.hpp
src/mbgl/style/style.cpp
- src/mbgl/style/style.hpp
- src/mbgl/style/tile_source_impl.cpp
- src/mbgl/style/tile_source_impl.hpp
+ src/mbgl/style/style_impl.cpp
+ src/mbgl/style/style_impl.hpp
src/mbgl/style/types.cpp
- src/mbgl/style/update_batch.hpp
# style/conversion
include/mbgl/style/conversion/constant.hpp
+ include/mbgl/style/conversion/coordinate.hpp
include/mbgl/style/conversion/data_driven_property_value.hpp
include/mbgl/style/conversion/filter.hpp
include/mbgl/style/conversion/function.hpp
@@ -368,6 +380,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/conversion/tileset.hpp
include/mbgl/style/conversion/transition_options.hpp
src/mbgl/style/conversion/geojson.cpp
+ src/mbgl/style/conversion/json.hpp
src/mbgl/style/conversion/stringify.hpp
# style/function
@@ -434,11 +447,15 @@ set(MBGL_CORE_FILES
# style/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
src/mbgl/style/sources/geojson_source.cpp
src/mbgl/style/sources/geojson_source_impl.cpp
src/mbgl/style/sources/geojson_source_impl.hpp
+ src/mbgl/style/sources/image_source.cpp
+ src/mbgl/style/sources/image_source_impl.cpp
+ src/mbgl/style/sources/image_source_impl.hpp
src/mbgl/style/sources/raster_source.cpp
src/mbgl/style/sources/raster_source_impl.cpp
src/mbgl/style/sources/raster_source_impl.hpp
@@ -460,7 +477,9 @@ set(MBGL_CORE_FILES
src/mbgl/text/glyph.hpp
src/mbgl/text/glyph_atlas.cpp
src/mbgl/text/glyph_atlas.hpp
- src/mbgl/text/glyph_atlas_observer.hpp
+ src/mbgl/text/glyph_manager.cpp
+ src/mbgl/text/glyph_manager.hpp
+ src/mbgl/text/glyph_manager_observer.hpp
src/mbgl/text/glyph_pbf.cpp
src/mbgl/text/glyph_pbf.hpp
src/mbgl/text/glyph_range.hpp
@@ -471,6 +490,7 @@ set(MBGL_CORE_FILES
src/mbgl/text/shaping.hpp
# tile
+ include/mbgl/tile/tile_id.hpp
src/mbgl/tile/geojson_tile.cpp
src/mbgl/tile/geojson_tile.hpp
src/mbgl/tile/geometry_tile.cpp
@@ -487,13 +507,15 @@ 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
src/mbgl/tile/tile_observer.hpp
src/mbgl/tile/vector_tile.cpp
src/mbgl/tile/vector_tile.hpp
+ src/mbgl/tile/vector_tile_data.cpp
+ src/mbgl/tile/vector_tile_data.hpp
# util
include/mbgl/util/any.hpp
@@ -515,6 +537,7 @@ set(MBGL_CORE_FILES
include/mbgl/util/geometry.hpp
include/mbgl/util/ignore.hpp
include/mbgl/util/image.hpp
+ include/mbgl/util/immutable.hpp
include/mbgl/util/indexed_tuple.hpp
include/mbgl/util/interpolate.hpp
include/mbgl/util/logging.hpp
@@ -527,6 +550,7 @@ 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
@@ -549,7 +573,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
@@ -564,6 +588,7 @@ set(MBGL_CORE_FILES
src/mbgl/util/io.cpp
src/mbgl/util/io.hpp
src/mbgl/util/logging.cpp
+ src/mbgl/util/longest_common_subsequence.hpp
src/mbgl/util/mapbox.cpp
src/mbgl/util/mapbox.hpp
src/mbgl/util/mat2.cpp
@@ -582,9 +607,6 @@ set(MBGL_CORE_FILES
src/mbgl/util/stopwatch.cpp
src/mbgl/util/stopwatch.hpp
src/mbgl/util/string.cpp
- src/mbgl/util/thread.hpp
- src/mbgl/util/thread_context.cpp
- src/mbgl/util/thread_context.hpp
src/mbgl/util/thread_local.hpp
src/mbgl/util/throttler.cpp
src/mbgl/util/throttler.hpp
@@ -597,7 +619,5 @@ set(MBGL_CORE_FILES
src/mbgl/util/utf.hpp
src/mbgl/util/version.cpp
src/mbgl/util/version.hpp
- src/mbgl/util/work_queue.cpp
- src/mbgl/util/work_queue.hpp
src/mbgl/util/work_request.cpp
)
diff --git a/cmake/core.cmake b/cmake/core.cmake
index 531edc092d..c4e711f558 100644
--- a/cmake/core.cmake
+++ b/cmake/core.cmake
@@ -26,7 +26,13 @@ target_add_mason_package(mbgl-core PRIVATE earcut)
target_add_mason_package(mbgl-core PRIVATE protozero)
target_add_mason_package(mbgl-core PRIVATE polylabel)
target_add_mason_package(mbgl-core PRIVATE wagyu)
+target_add_mason_package(mbgl-core PRIVATE shelf-pack)
+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/glfw.cmake b/cmake/glfw.cmake
index c14bd6f2c2..29d8d2ba94 100644
--- a/cmake/glfw.cmake
+++ b/cmake/glfw.cmake
@@ -5,22 +5,13 @@ 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
)
-# 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
)
@@ -33,8 +24,27 @@ target_link_libraries(mbgl-glfw
PRIVATE mbgl-core
)
+target_add_mason_package(mbgl-glfw PRIVATE cheap-ruler)
+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)
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..46511d2548 100644
--- a/cmake/loop-darwin.cmake
+++ b/cmake/loop-darwin.cmake
@@ -17,3 +17,5 @@ target_include_directories(mbgl-loop-darwin
)
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 0f55fce64c..8840040963 100644
--- a/cmake/loop-uv.cmake
+++ b/cmake/loop-uv.cmake
@@ -14,4 +14,10 @@ target_include_directories(mbgl-loop-uv
PRIVATE src
)
+target_link_libraries(mbgl-loop-uv
+ PRIVATE mbgl-core
+)
+
create_source_groups(mbgl-loop-uv)
+
+xcode_create_scheme(TARGET mbgl-loop-uv) \ No newline at end of file
diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake
index 14e5b0d691..7f7d820a68 100644
--- a/cmake/mbgl.cmake
+++ b/cmake/mbgl.cmake
@@ -44,6 +44,12 @@ execute_process(
COMMAND git submodule update --init mapbox-gl-js
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
+if(MBGL_PLATFORM STREQUAL "ios")
+ execute_process(
+ COMMAND git submodule update --init platform/ios/vendor/SMCalloutView platform/ios/uitest/KIF platform/ios/uitest/OHHTTPStubs
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
+endif()
+
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/mapbox-gl-js/node_modules")
# Symlink mapbox-gl-js/node_modules so that the modules that are
# about to be installed get cached between CI runs.
@@ -108,6 +114,32 @@ 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)
+
+ # -Wno-unknown-pragmas
+ set_xcode_property(${target} GCC_WARN_UNKNOWN_PRAGMAS YES)
+
+ # -Wnon-virtual-dtor
+ set_xcode_property(${target} GCC_WARN_NON_VIRTUAL_DESTRUCTOR YES)
+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..5a6c7595ad 100644
--- a/cmake/node.cmake
+++ b/cmake/node.cmake
@@ -53,3 +53,50 @@ 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
+ "`npm bin tape`/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-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 a79b22db54..c9a3349792 100644
--- a/cmake/offline.cmake
+++ b/cmake/offline.cmake
@@ -24,3 +24,18 @@ target_add_mason_package(mbgl-offline PRIVATE boost_libprogram_options)
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..f69aed16c0 100644
--- a/cmake/render.cmake
+++ b/cmake/render.cmake
@@ -20,3 +20,24 @@ target_add_mason_package(mbgl-render PRIVATE boost_libprogram_options)
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 c29bd284d7..027c34f31e 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -10,17 +10,15 @@ 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_layer.test.cpp
test/api/query.test.cpp
- test/api/render_missing.test.cpp
- test/api/repeated_render.test.cpp
-
- # geometry
- test/geometry/binpack.test.cpp
+ test/api/recycle_map.cpp
+ test/api/zoom_history.cpp
# gl
test/gl/bucket.test.cpp
@@ -31,6 +29,7 @@ set(MBGL_TEST_FILES
# map
test/map/map.test.cpp
+ test/map/prefetch.test.cpp
test/map/transform.test.cpp
# math
@@ -40,13 +39,15 @@ set(MBGL_TEST_FILES
# programs
test/programs/binary_program.test.cpp
+ 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
# sprite
- test/sprite/sprite_atlas.test.cpp
+ test/sprite/sprite_loader.test.cpp
test/sprite/sprite_parser.test.cpp
# src/mbgl/test
@@ -93,10 +94,12 @@ set(MBGL_TEST_FILES
# style/function
test/style/function/camera_function.test.cpp
test/style/function/composite_function.test.cpp
+ test/style/function/exponential_stops.test.cpp
+ test/style/function/interval_stops.test.cpp
test/style/function/source_function.test.cpp
# style
- test/style/paint_property.test.cpp
+ test/style/properties.test.cpp
test/style/source.test.cpp
test/style/style.test.cpp
test/style/style_image.test.cpp
@@ -104,7 +107,7 @@ set(MBGL_TEST_FILES
test/style/style_parser.test.cpp
# text
- test/text/glyph_atlas.test.cpp
+ test/text/glyph_loader.test.cpp
test/text/glyph_pbf.test.cpp
test/text/quads.test.cpp
@@ -119,6 +122,7 @@ set(MBGL_TEST_FILES
# util
test/util/async_task.test.cpp
+ test/util/dtoa.test.cpp
test/util/geo.test.cpp
test/util/http_timeout.test.cpp
test/util/image.test.cpp
@@ -137,5 +141,4 @@ set(MBGL_TEST_FILES
test/util/timer.test.cpp
test/util/token.test.cpp
test/util/url.test.cpp
- test/util/work_queue.test.cpp
)
diff --git a/cmake/test.cmake b/cmake/test.cmake
index 3c63f7bcf8..c821d53316 100644
--- a/cmake/test.cmake
+++ b/cmake/test.cmake
@@ -35,7 +35,21 @@ target_add_mason_package(mbgl-test PRIVATE pixelmatch)
target_add_mason_package(mbgl-test PRIVATE boost)
target_add_mason_package(mbgl-test PRIVATE geojson)
target_add_mason_package(mbgl-test PRIVATE geojsonvt)
+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/src/mbgl/actor/actor.hpp b/include/mbgl/actor/actor.hpp
index 810114c513..a0df19208e 100644
--- a/src/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/src/mbgl/actor/actor_ref.hpp b/include/mbgl/actor/actor_ref.hpp
index 9d858d823f..958ee3777c 100644
--- a/src/mbgl/actor/actor_ref.hpp
+++ b/include/mbgl/actor/actor_ref.hpp
@@ -24,19 +24,40 @@ template <class Object>
class ActorRef {
public:
ActorRef(Object& object_, std::weak_ptr<Mailbox> weakMailbox_)
- : object(object_),
+ : object(&object_),
weakMailbox(std::move(weakMailbox_)) {
}
template <typename Fn, class... Args>
void invoke(Fn fn, Args&&... args) {
if (auto mailbox = weakMailbox.lock()) {
- mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...));
+ 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();
+
+ 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;
+ Object* object;
std::weak_ptr<Mailbox> weakMailbox;
};
diff --git a/include/mbgl/actor/mailbox.hpp b/include/mbgl/actor/mailbox.hpp
index cff0de243a..8ecf91701a 100644
--- a/include/mbgl/actor/mailbox.hpp
+++ b/include/mbgl/actor/mailbox.hpp
@@ -23,8 +23,10 @@ public:
private:
Scheduler& scheduler;
- std::mutex closingMutex;
- bool closing { false };
+ std::recursive_mutex receivingMutex;
+ std::mutex pushingMutex;
+
+ bool closed { false };
std::mutex queueMutex;
std::queue<std::unique_ptr<Message>> queue;
diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp
new file mode 100644
index 0000000000..0a20993352
--- /dev/null
+++ b/include/mbgl/actor/message.hpp
@@ -0,0 +1,108 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+
+#include <future>
+#include <utility>
+
+namespace mbgl {
+
+// A movable type-erasing function wrapper. This allows to store arbitrary invokable
+// things (like std::function<>, or the result of a movable-only std::bind()) in the queue.
+// Source: http://stackoverflow.com/a/29642072/331379
+class Message {
+public:
+ virtual ~Message() = default;
+ virtual void operator()() = 0;
+};
+
+template <class Object, class MemberFn, class ArgsTuple>
+class MessageImpl : public Message {
+public:
+ MessageImpl(Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
+ : object(object_),
+ memberFn(memberFn_),
+ argsTuple(std::move(argsTuple_)) {
+ }
+
+ void operator()() override {
+ invoke(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>());
+ }
+
+ template <std::size_t... I>
+ void invoke(std::index_sequence<I...>) {
+ (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
+ }
+
+ Object& object;
+ MemberFn memberFn;
+ 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>
+std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&... args) {
+ auto tuple = std::make_tuple(std::forward<Args>(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 de83d24712..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,31 +33,41 @@ 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::PropertyValue<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:
- ShapeAnnotationGeometry geometry;
- style::DataDrivenPropertyValue<float> opacity { 1.0f };
- style::DataDrivenPropertyValue<Color> color { Color::black() };
- style::DataDrivenPropertyValue<Color> outlineColor {};
-};
+ 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_)) {}
-// An annotation whose type and properties are sourced from a style layer.
-class StyleSourcedAnnotation {
-public:
ShapeAnnotationGeometry geometry;
- std::string layerID;
+ style::DataDrivenPropertyValue<float> opacity;
+ style::DataDrivenPropertyValue<Color> color;
+ style::DataDrivenPropertyValue<Color> outlineColor;
};
using Annotation = variant<
SymbolAnnotation,
LineAnnotation,
- FillAnnotation,
- StyleSourcedAnnotation>;
+ FillAnnotation>;
} // namespace mbgl
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index bbeeeac6cc..4108725776 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -4,14 +4,10 @@
#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/style/transition_options.hpp>
#include <mbgl/map/camera.hpp>
-#include <mbgl/map/query.hpp>
#include <cstdint>
#include <string>
@@ -21,58 +17,41 @@
namespace mbgl {
-class Backend;
-class View;
class FileSource;
class Scheduler;
+class RendererFrontend;
namespace style {
class Image;
-class Source;
-class Layer;
-class Light;
+class Style;
} // namespace 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;
- // Styling
- void addClass(const std::string&);
- void removeClass(const std::string&);
- void setClasses(const std::vector<std::string>&);
-
- style::TransitionOptions getTransitionOptions() const;
- void setTransitionOptions(const style::TransitionOptions&);
-
- bool hasClass(const std::string&) const;
- std::vector<std::string> getClasses() const;
-
- void setStyleURL(const std::string&);
- void setStyleJSON(const std::string&);
- std::string getStyleURL() const;
- std::string getStyleJSON() const;
+ void setStyle(std::unique_ptr<style::Style>);
// Transition
void cancelTransitions();
@@ -155,7 +134,7 @@ public:
LatLng latLngForPixel(const ScreenCoordinate&) const;
// Annotations
- void addAnnotationImage(const std::string&, std::unique_ptr<style::Image>);
+ void addAnnotationImage(std::unique_ptr<style::Image>);
void removeAnnotationImage(const std::string&);
double getTopOffsetPixelsForAnnotationImage(const std::string&);
@@ -163,44 +142,14 @@ public:
void updateAnnotation(AnnotationID, const Annotation&);
void removeAnnotation(AnnotationID);
- // Sources
- std::vector<style::Source*> getSources();
- style::Source* getSource(const std::string& sourceID);
- void addSource(std::unique_ptr<style::Source>);
- std::unique_ptr<style::Source> removeSource(const std::string& sourceID);
-
- // Layers
- std::vector<style::Layer*> getLayers();
- style::Layer* getLayer(const std::string& layerID);
- void addLayer(std::unique_ptr<style::Layer>, const optional<std::string>& beforeLayerID = {});
- std::unique_ptr<style::Layer> removeLayer(const std::string& layerID);
-
- // Images
- void addImage(const std::string&, std::unique_ptr<style::Image>);
- void removeImage(const std::string&);
- const style::Image* getImage(const std::string&);
-
- // Light
- void setLight(std::unique_ptr<style::Light>);
- style::Light* getLight();
-
- // Defaults
- std::string getStyleName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
-
- // 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..05de2df22c 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>
@@ -51,23 +52,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 322c17b3fc..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,7 +15,7 @@ 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.
@@ -27,7 +27,7 @@ private:
BackendScope* priorScope;
BackendScope* nextScope;
- Backend& backend;
+ RendererBackend& backend;
const ScopeType scopeType;
bool activated = false;
};
diff --git a/include/mbgl/map/query.hpp b/include/mbgl/renderer/query.hpp
index 9fac60d71d..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,10 +29,15 @@ 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;
optional<style::Filter> filter;
};
-}
+} // namespace mbgl
diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp
new file mode 100644
index 0000000000..be8abb2c29
--- /dev/null
+++ b/include/mbgl/renderer/renderer.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <mbgl/map/mode.hpp>
+#include <mbgl/renderer/query.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 = {});
+ ~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;
+
+ // 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 69a998709b..295838c71b 100644
--- a/include/mbgl/map/backend.hpp
+++ b/include/mbgl/renderer/renderer_backend.hpp
@@ -1,6 +1,6 @@
#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>
@@ -16,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();
@@ -29,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;
// Called when the backend's GL context needs to be made active or inactive. These are called,
- // as a matched pair, in four 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
+ // as a matched pair, exclusively through BackendScope, in two situations:
//
- // 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;
@@ -62,7 +64,8 @@ protected:
// Tells the renderer that OpenGL state has already been set by the windowing toolkit.
// It sets the internal assumed state to the supplied values.
void assumeFramebufferBinding(gl::FramebufferID fbo);
- void assumeViewportSize(const Size&);
+ void assumeViewport(int32_t x, int32_t y, const Size&);
+ void assumeScissorTest(bool);
// Returns true when assumed framebuffer binding hasn't changed from the implicit binding.
bool implicitFramebufferBound();
@@ -70,7 +73,8 @@ protected:
// Triggers an OpenGL state update if the internal assumed state doesn't match the
// supplied values.
void setFramebufferBinding(gl::FramebufferID fbo);
- void setViewportSize(const Size&);
+ void setViewport(int32_t x, int32_t y, const Size&);
+ void setScissorTest(bool);
protected:
std::unique_ptr<gl::Context> context;
@@ -81,7 +85,7 @@ private:
friend class BackendScope;
};
-constexpr bool operator==(const Backend& a, const Backend& b) {
+MBGL_CONSTEXPR bool operator==(const RendererBackend& a, const RendererBackend& b) {
return &a == &b;
}
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 f612a01aac..91e442cf85 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -1,10 +1,13 @@
#pragma once
+#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/constants.hpp>
+#include <mbgl/util/optional.hpp>
#include <vector>
+#include <mutex>
namespace mbgl {
@@ -12,6 +15,8 @@ namespace util {
template <typename T> class Thread;
} // namespace util
+class ResourceTransform;
+
class DefaultFileSource : public FileSource {
public:
/*
@@ -34,12 +39,12 @@ public:
}
void setAPIBaseURL(const std::string&);
- std::string getAPIBaseURL() const;
+ std::string getAPIBaseURL();
void setAccessToken(const std::string&);
- std::string getAccessToken() const;
+ std::string getAccessToken();
- void setResourceTransform(std::function<std::string(Resource::Kind, std::string&&)>);
+ void setResourceTransform(optional<ActorRef<ResourceTransform>>&&);
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
@@ -135,15 +140,20 @@ public:
void resume();
// For testing only.
+ void setOnlineStatus(bool);
void put(const Resource&, const Response&);
class Impl;
private:
- const std::unique_ptr<util::Thread<Impl>> thread;
- const std::unique_ptr<FileSource> assetFileSource;
- const std::unique_ptr<FileSource> localFileSource;
+ // Shared so destruction is done on this thread
+ const std::shared_ptr<FileSource> assetFileSource;
+ const std::unique_ptr<util::Thread<Impl>> impl;
+
+ std::mutex cachedBaseURLMutex;
std::string cachedBaseURL = mbgl::util::API_BASE_URL;
+
+ std::mutex cachedAccessTokenMutex;
std::string cachedAccessToken;
};
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index 818cfe2ba5..117dd0591b 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -31,12 +31,14 @@ public:
/* Private */
std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
-
+ uint64_t tileCount(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
const std::string styleURL;
const LatLngBounds bounds;
const double minZoom;
const double maxZoom;
const float pixelRatio;
+private:
+ Range<uint8_t> coveringZoomRange(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
};
/*
diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp
index 51cfc5a2a1..28d70ce544 100644
--- a/include/mbgl/storage/online_file_source.hpp
+++ b/include/mbgl/storage/online_file_source.hpp
@@ -1,10 +1,14 @@
#pragma once
+#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/constants.hpp>
+#include <mbgl/util/optional.hpp>
namespace mbgl {
+class ResourceTransform;
+
class OnlineFileSource : public FileSource {
public:
OnlineFileSource();
@@ -16,12 +20,13 @@ public:
void setAccessToken(const std::string& t) { accessToken = t; }
std::string getAccessToken() const { return accessToken; }
- using ResourceTransform =
- std::function<std::unique_ptr<AsyncRequest>(Resource::Kind, std::string&&, std::function<void(std::string&&)>)>;
- void setResourceTransform(ResourceTransform&& cb);
+ void setResourceTransform(optional<ActorRef<ResourceTransform>>&&);
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 c05f40b65c..5d44f4869f 100644
--- a/include/mbgl/storage/resource.hpp
+++ b/include/mbgl/storage/resource.hpp
@@ -18,7 +18,8 @@ public:
Tile,
Glyphs,
SpriteImage,
- SpriteJSON
+ SpriteJSON,
+ Image
};
struct TileData {
@@ -55,7 +56,8 @@ public:
const std::pair<uint16_t, uint16_t>& glyphRange);
static Resource spriteImage(const std::string& base, float pixelRatio);
static Resource spriteJSON(const std::string& base, float pixelRatio);
-
+ static Resource image(const std::string& url);
+
Kind kind;
Necessity necessity;
std::string url;
@@ -66,6 +68,7 @@ public:
optional<Timestamp> priorModified = {};
optional<Timestamp> priorExpires = {};
optional<std::string> priorEtag = {};
+ std::shared_ptr<const std::string> priorData;
};
} // namespace mbgl
diff --git a/include/mbgl/storage/resource_transform.hpp b/include/mbgl/storage/resource_transform.hpp
new file mode 100644
index 0000000000..738c497176
--- /dev/null
+++ b/include/mbgl/storage/resource_transform.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/storage/resource.hpp>
+
+#include <functional>
+#include <string>
+
+namespace mbgl {
+
+class Mailbox;
+
+class ResourceTransform {
+public:
+ using TransformCallback = std::function<std::string(Resource::Kind kind, const std::string&& url)>;
+ using FinishedCallback = std::function<void(const std::string&&)>;
+
+ ResourceTransform(ActorRef<ResourceTransform>, TransformCallback&&);
+
+ void transform(Resource::Kind, const std::string&& url, FinishedCallback&&);
+
+private:
+ TransformCallback transformCallback;
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp
index 32fe4e0c8a..711f008e83 100644
--- a/include/mbgl/storage/response.hpp
+++ b/include/mbgl/storage/response.hpp
@@ -27,6 +27,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 +41,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 d6fb3a6dd0..27504a89b1 100644
--- a/include/mbgl/style/conversion.hpp
+++ b/include/mbgl/style/conversion.hpp
@@ -46,6 +46,7 @@ namespace conversion {
* `toBool(v)` -- returns `optional<bool>`, absence indicating `v` is not a JSON boolean
* `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,
absence indicating `v` is not a boolean, number, or string. Numbers should be converted to
diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp
new file mode 100644
index 0000000000..732624e77f
--- /dev/null
+++ b/include/mbgl/style/conversion/coordinate.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/geo.hpp>
+
+namespace mbgl {
+namespace style {
+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);
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp
index 1f8f0fd161..986d1bf80d 100644
--- a/include/mbgl/style/conversion/filter.hpp
+++ b/include/mbgl/style/conversion/filter.hpp
@@ -57,7 +57,7 @@ public:
return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
}
- error = { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" };
+ error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
return {};
}
diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp
index fa8af1e2be..752b6dd045 100644
--- a/include/mbgl/style/conversion/function.hpp
+++ b/include/mbgl/style/conversion/function.hpp
@@ -156,7 +156,7 @@ 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";
+ std::string type = util::Interpolatable<T>::value ? "exponential" : "interval";
auto typeValue = objectMember(value, "type");
if (typeValue && toString(*typeValue)) {
@@ -218,7 +218,7 @@ optional<optional<T>> convertDefaultValue(const V& value, Error& error) {
auto defaultValue = convert<T>(*defaultValueValue, error);
if (!defaultValue) {
- error = { "wrong type for \"default\": " + error.message };
+ error = { R"(wrong type for "default": )" + error.message };
return {};
}
diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp
index 3a64c36bf5..1fe467165d 100644
--- a/include/mbgl/style/conversion/layer.hpp
+++ b/include/mbgl/style/conversion/layer.hpp
@@ -28,30 +28,23 @@ optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const V
}
template <class V>
-optional<Error> setPaintProperty(Layer& layer, const std::string& name, const V& value, const optional<std::string>& klass) {
+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, klass);
+ return it->second(layer, value);
}
template <class V>
optional<Error> setPaintProperties(Layer& layer, const V& value) {
- return eachMember(value, [&] (const std::string& paintName, const V& paintValue) -> optional<Error> {
- if (paintName.compare(0, 5, "paint") != 0) {
- return {};
- }
-
- optional<std::string> klass;
- if (paintName.compare(0, 6, "paint.") == 0) {
- klass = paintName.substr(6);
- }
-
- return eachMember(paintValue, [&] (const std::string& k, const V& v) {
- return setPaintProperty(layer, k, v, klass);
- });
+ auto paintValue = objectMember(value, "paint");
+ if (!paintValue) {
+ return {};
+ }
+ return eachMember(*paintValue, [&] (const std::string& k, const V& v) {
+ return setPaintProperty(layer, k, v);
});
}
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp
index 105cca99d6..59b0e7be32 100644
--- a/include/mbgl/style/conversion/make_property_setters.hpp
+++ b/include/mbgl/style/conversion/make_property_setters.hpp
@@ -20,50 +20,52 @@ namespace conversion {
template <class V>
auto makeLayoutPropertySetters() {
- std::unordered_map<std::string, LayoutPropertySetter<V>> result;
+ std::unordered_map<std::string, PropertySetter<V>> result;
result["visibility"] = &setVisibility<V>;
- result["line-cap"] = &setLayoutProperty<V, LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
- result["line-join"] = &setLayoutProperty<V, LineLayer, PropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
- result["line-miter-limit"] = &setLayoutProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
- result["line-round-limit"] = &setLayoutProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
-
- result["symbol-placement"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
- result["symbol-spacing"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
- result["symbol-avoid-edges"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
- result["icon-allow-overlap"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
- result["icon-ignore-placement"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
- result["icon-optional"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
- result["icon-rotation-alignment"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
- result["icon-size"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
- result["icon-text-fit"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
- result["icon-text-fit-padding"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
- result["icon-image"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
- result["icon-rotate"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
- result["icon-padding"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
- result["icon-keep-upright"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
- result["icon-offset"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
- result["text-pitch-alignment"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
- result["text-rotation-alignment"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
- result["text-field"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
- result["text-font"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
- result["text-size"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
- result["text-max-width"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
- result["text-line-height"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
- result["text-letter-spacing"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
- result["text-justify"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
- result["text-anchor"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<TextAnchorType>, &SymbolLayer::setTextAnchor>;
- result["text-max-angle"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
- result["text-rotate"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
- result["text-padding"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
- result["text-keep-upright"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
- result["text-transform"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
- result["text-offset"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
- result["text-allow-overlap"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
- result["text-ignore-placement"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
- result["text-optional"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
+ result["line-cap"] = &setProperty<V, LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
+ result["line-join"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
+ result["line-miter-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
+ result["line-round-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
+
+ result["symbol-placement"] = &setProperty<V, SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
+ result["symbol-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
+ result["symbol-avoid-edges"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
+ result["icon-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
+ result["icon-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
+ result["icon-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
+ result["icon-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
+ result["icon-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
+ result["icon-text-fit"] = &setProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
+ result["icon-text-fit-padding"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
+ result["icon-image"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
+ result["icon-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
+ result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
+ result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
+ result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
+ result["icon-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
+ result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
+ result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
+ result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
+ result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
+ result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
+ result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
+ result["text-max-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
+ result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
+ result["text-letter-spacing"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
+ result["text-justify"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
+ result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
+ result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
+ result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
+ result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
+ result["text-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
+ result["text-transform"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
+ result["text-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
+ result["text-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
+ result["text-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
+ result["text-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
@@ -74,129 +76,131 @@ auto makeLayoutPropertySetters() {
template <class V>
auto makePaintPropertySetters() {
- std::unordered_map<std::string, PaintPropertySetter<V>> result;
+ std::unordered_map<std::string, PropertySetter<V>> result;
- result["fill-antialias"] = &setPaintProperty<V, FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
+ result["fill-antialias"] = &setProperty<V, FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
result["fill-antialias-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillAntialiasTransition>;
- result["fill-opacity"] = &setPaintProperty<V, FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
+ result["fill-opacity"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
result["fill-opacity-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOpacityTransition>;
- result["fill-color"] = &setPaintProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
+ result["fill-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
result["fill-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillColorTransition>;
- result["fill-outline-color"] = &setPaintProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
+ result["fill-outline-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
result["fill-outline-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOutlineColorTransition>;
- result["fill-translate"] = &setPaintProperty<V, FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>;
+ 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"] = &setPaintProperty<V, FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
+ result["fill-translate-anchor"] = &setProperty<V, FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
result["fill-translate-anchor-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateAnchorTransition>;
- result["fill-pattern"] = &setPaintProperty<V, FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
+ result["fill-pattern"] = &setProperty<V, FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
result["fill-pattern-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillPatternTransition>;
- result["line-opacity"] = &setPaintProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
+ result["line-opacity"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
result["line-opacity-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOpacityTransition>;
- result["line-color"] = &setPaintProperty<V, LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
+ result["line-color"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
result["line-color-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineColorTransition>;
- result["line-translate"] = &setPaintProperty<V, LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>;
+ 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"] = &setPaintProperty<V, LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
+ result["line-translate-anchor"] = &setProperty<V, LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
result["line-translate-anchor-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateAnchorTransition>;
- result["line-width"] = &setPaintProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineWidth>;
+ result["line-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>;
result["line-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineWidthTransition>;
- result["line-gap-width"] = &setPaintProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
+ result["line-gap-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
result["line-gap-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineGapWidthTransition>;
- result["line-offset"] = &setPaintProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
+ result["line-offset"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
result["line-offset-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOffsetTransition>;
- result["line-blur"] = &setPaintProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
+ result["line-blur"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
result["line-blur-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineBlurTransition>;
- result["line-dasharray"] = &setPaintProperty<V, LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
+ result["line-dasharray"] = &setProperty<V, LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
result["line-dasharray-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineDasharrayTransition>;
- result["line-pattern"] = &setPaintProperty<V, LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
+ result["line-pattern"] = &setProperty<V, LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
result["line-pattern-transition"] = &setTransition<V, LineLayer, &LineLayer::setLinePatternTransition>;
- result["icon-opacity"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
+ result["icon-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
result["icon-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconOpacityTransition>;
- result["icon-color"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
+ result["icon-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
result["icon-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconColorTransition>;
- result["icon-halo-color"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>;
+ 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"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>;
+ 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"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
+ result["icon-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
result["icon-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>;
- result["icon-translate"] = &setPaintProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>;
+ 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"] = &setPaintProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
+ result["icon-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
result["icon-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>;
- result["text-opacity"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
+ result["text-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
result["text-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextOpacityTransition>;
- result["text-color"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
+ result["text-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
result["text-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextColorTransition>;
- result["text-halo-color"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>;
+ 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"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>;
+ 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"] = &setPaintProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
+ result["text-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
result["text-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>;
- result["text-translate"] = &setPaintProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>;
+ 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"] = &setPaintProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
+ result["text-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
result["text-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>;
- result["circle-radius"] = &setPaintProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
+ result["circle-radius"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
result["circle-radius-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleRadiusTransition>;
- result["circle-color"] = &setPaintProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
+ result["circle-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
result["circle-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleColorTransition>;
- result["circle-blur"] = &setPaintProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
+ result["circle-blur"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
result["circle-blur-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleBlurTransition>;
- result["circle-opacity"] = &setPaintProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
+ result["circle-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
result["circle-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleOpacityTransition>;
- result["circle-translate"] = &setPaintProperty<V, CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>;
+ 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"] = &setPaintProperty<V, CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>;
+ 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"] = &setPaintProperty<V, CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>;
+ 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"] = &setPaintProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
+ result["circle-pitch-alignment"] = &setProperty<V, CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>;
+ result["circle-pitch-alignment-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>;
+ result["circle-stroke-width"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
result["circle-stroke-width-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
- result["circle-stroke-color"] = &setPaintProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
+ 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"] = &setPaintProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
+ 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"] = &setPaintProperty<V, FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
+ 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"] = &setPaintProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
+ 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"] = &setPaintProperty<V, FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
+ 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"] = &setPaintProperty<V, FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
+ 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"] = &setPaintProperty<V, FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
+ 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"] = &setPaintProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
+ 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"] = &setPaintProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
+ result["fill-extrusion-base"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
result["fill-extrusion-base-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
- result["raster-opacity"] = &setPaintProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
+ result["raster-opacity"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
result["raster-opacity-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterOpacityTransition>;
- result["raster-hue-rotate"] = &setPaintProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
+ 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"] = &setPaintProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
+ 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"] = &setPaintProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
+ result["raster-brightness-max"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
result["raster-brightness-max-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
- result["raster-saturation"] = &setPaintProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
+ result["raster-saturation"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
result["raster-saturation-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterSaturationTransition>;
- result["raster-contrast"] = &setPaintProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
+ result["raster-contrast"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
result["raster-contrast-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterContrastTransition>;
- result["raster-fade-duration"] = &setPaintProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
+ result["raster-fade-duration"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
result["raster-fade-duration-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
- result["background-color"] = &setPaintProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
+ result["background-color"] = &setProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
result["background-color-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
- result["background-pattern"] = &setPaintProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
+ result["background-pattern"] = &setProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
result["background-pattern-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
- result["background-opacity"] = &setPaintProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
+ result["background-opacity"] = &setProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
result["background-opacity-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
return result;
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp.ejs b/include/mbgl/style/conversion/make_property_setters.hpp.ejs
index a99e75aec7..19c9f70538 100644
--- a/include/mbgl/style/conversion/make_property_setters.hpp.ejs
+++ b/include/mbgl/style/conversion/make_property_setters.hpp.ejs
@@ -16,13 +16,13 @@ namespace conversion {
template <class V>
auto makeLayoutPropertySetters() {
- std::unordered_map<std::string, LayoutPropertySetter<V>> result;
+ 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 %>"] = &setLayoutProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
+ result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
<% } -%>
<% } -%>
@@ -31,11 +31,11 @@ auto makeLayoutPropertySetters() {
template <class V>
auto makePaintPropertySetters() {
- std::unordered_map<std::string, PaintPropertySetter<V>> result;
+ std::unordered_map<std::string, PropertySetter<V>> result;
<% for (const layer of locals.layers) { -%>
<% for (const property of layer.paintProperties) { -%>
- result["<%- property.name %>"] = &setPaintProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
+ 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>;
<% } -%>
diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp
index 1f537f3c4d..759c4512cc 100644
--- a/include/mbgl/style/conversion/property_setter.hpp
+++ b/include/mbgl/style/conversion/property_setter.hpp
@@ -14,14 +14,11 @@ namespace style {
namespace conversion {
template <class V>
-using LayoutPropertySetter = optional<Error> (*) (Layer&, const V&);
-
-template <class V>
-using PaintPropertySetter = optional<Error> (*) (Layer&, const V&, const optional<std::string>&);
+using PropertySetter = optional<Error> (*) (Layer&, const V&);
template <class V, class L, class PropertyValue, void (L::*setter)(PropertyValue)>
-optional<Error> setLayoutProperty(Layer& layer, const V& value) {
- L* typedLayer = layer.as<L>();
+optional<Error> setProperty(Layer& layer, const V& value) {
+ auto* typedLayer = layer.as<L>();
if (!typedLayer) {
return Error { "layer doesn't support this property" };
}
@@ -36,26 +33,9 @@ optional<Error> setLayoutProperty(Layer& layer, const V& value) {
return {};
}
-template <class V, class L, class PropertyValue, void (L::*setter)(PropertyValue, const optional<std::string>&)>
-optional<Error> setPaintProperty(Layer& layer, const V& value, const optional<std::string>& klass) {
- L* typedLayer = layer.as<L>();
- if (!typedLayer) {
- return Error { "layer doesn't support this property" };
- }
-
- Error error;
- optional<PropertyValue> typedValue = convert<PropertyValue>(value, error);
- if (!typedValue) {
- return error;
- }
-
- (typedLayer->*setter)(*typedValue, klass);
- return {};
-}
-
-template <class V, class L, void (L::*setter)(const TransitionOptions&, const optional<std::string>&)>
-optional<Error> setTransition(Layer& layer, const V& value, const optional<std::string>& klass) {
- L* typedLayer = layer.as<L>();
+template <class V, class L, void (L::*setter)(const TransitionOptions&)>
+optional<Error> setTransition(Layer& layer, const V& value) {
+ auto* typedLayer = layer.as<L>();
if (!typedLayer) {
return Error { "layer doesn't support this property" };
}
@@ -66,7 +46,7 @@ optional<Error> setTransition(Layer& layer, const V& value, const optional<std::
return error;
}
- (typedLayer->*setter)(*transition, klass);
+ (typedLayer->*setter)(*transition);
return {};
}
diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp
index dc7cdc0d42..e0563ac10b 100644
--- a/include/mbgl/style/conversion/source.hpp
+++ b/include/mbgl/style/conversion/source.hpp
@@ -1,6 +1,7 @@
#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>
@@ -8,6 +9,8 @@
#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 {
@@ -16,6 +19,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)) {
@@ -41,6 +45,8 @@ public:
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 {};
@@ -136,6 +142,47 @@ private:
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) };
+ }
};
} // namespace conversion
diff --git a/include/mbgl/style/data_driven_property_value.hpp b/include/mbgl/style/data_driven_property_value.hpp
index 5acf800840..5d7c596363 100644
--- a/include/mbgl/style/data_driven_property_value.hpp
+++ b/include/mbgl/style/data_driven_property_value.hpp
@@ -49,16 +49,20 @@ public:
bool isZoomConstant() const {
return !value.template is<CameraFunction<T>>() && !value.template is<CompositeFunction<T>>();
}
-
+
template <class... Ts>
auto match(Ts&&... ts) const {
return value.match(std::forward<Ts>(ts)...);
}
template <typename Evaluator>
- auto evaluate(const Evaluator& evaluator) const {
+ auto evaluate(const Evaluator& evaluator, TimePoint = {}) const {
return Value::visit(value, evaluator);
}
+
+ bool hasDataDrivenPropertyDifference(const DataDrivenPropertyValue<T>& other) const {
+ return *this != other && (isDataDriven() || other.isDataDriven());
+ }
};
} // namespace style
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 2e4aac2238..7fde365b3d 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -12,7 +12,7 @@ template <class T>
class CameraFunction {
public:
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
IntervalStops<T>>,
@@ -25,7 +25,7 @@ public:
T evaluate(float zoom) const {
return stops.match([&] (const auto& s) {
- return s.evaluate(Value(double(zoom))).value_or(T());
+ return s.evaluate(zoom).value_or(T());
});
}
@@ -35,6 +35,7 @@ public:
}
Stops stops;
+ bool useIntegerZoom = false;
};
} // namespace style
diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp
index b82e63bc37..7b524b6021 100644
--- a/include/mbgl/style/function/composite_function.hpp
+++ b/include/mbgl/style/function/composite_function.hpp
@@ -24,7 +24,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 +34,7 @@ public:
CategoricalStops<T>>>;
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
CompositeExponentialStops<T>,
CompositeIntervalStops<T>,
@@ -67,7 +67,7 @@ public:
// 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->first > zoom) {
+ if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > zoom) {
minIt--;
}
@@ -135,6 +135,7 @@ public:
std::string property;
Stops stops;
optional<T> defaultValue;
+ bool useIntegerZoom = false;
private:
T evaluateFinal(const CoveringRanges& ranges, const Value& value, T finalDefaultValue) const {
diff --git a/include/mbgl/style/function/exponential_stops.hpp b/include/mbgl/style/function/exponential_stops.hpp
index 051f5aa9aa..b3866c4059 100644
--- a/include/mbgl/style/function/exponential_stops.hpp
+++ b/include/mbgl/style/function/exponential_stops.hpp
@@ -22,26 +22,28 @@ public:
base(base_) {
}
- optional<T> evaluate(const Value& value) const {
+ optional<T> evaluate(float z) const {
if (stops.empty()) {
- assert(false);
- return T();
- }
-
- optional<float> z = numericValue<float>(value);
- if (!z) {
- return T();
+ return {};
}
- auto it = stops.upper_bound(*z);
+ auto it = stops.upper_bound(z);
if (it == stops.end()) {
return stops.rbegin()->second;
} else if (it == stops.begin()) {
return stops.begin()->second;
} else {
return util::interpolate(std::prev(it)->second, it->second,
- util::interpolationFactor(base, { std::prev(it)->first, it->first }, *z));
+ util::interpolationFactor(base, { std::prev(it)->first, it->first }, z));
+ }
+ }
+
+ optional<T> evaluate(const Value& value) const {
+ optional<float> z = numericValue<float>(value);
+ if (!z) {
+ return {};
}
+ return evaluate(*z);
}
friend bool operator==(const ExponentialStops& lhs,
diff --git a/include/mbgl/style/function/interval_stops.hpp b/include/mbgl/style/function/interval_stops.hpp
index 50f2b48453..45e2dc6f2e 100644
--- a/include/mbgl/style/function/interval_stops.hpp
+++ b/include/mbgl/style/function/interval_stops.hpp
@@ -18,18 +18,12 @@ public:
: stops(std::move(stops_)) {
}
- optional<T> evaluate(const Value& value) const {
+ optional<T> evaluate(float z) const {
if (stops.empty()) {
- assert(false);
return {};
}
- optional<float> z = numericValue<float>(value);
- if (!z) {
- return {};
- }
-
- auto it = stops.upper_bound(*z);
+ auto it = stops.upper_bound(z);
if (it == stops.end()) {
return stops.rbegin()->second;
} else if (it == stops.begin()) {
@@ -39,6 +33,14 @@ public:
}
}
+ optional<T> evaluate(const Value& value) const {
+ optional<float> z = numericValue<float>(value);
+ if (!z) {
+ return {};
+ }
+ return evaluate(*z);
+ }
+
friend bool operator==(const IntervalStops& lhs,
const IntervalStops& rhs) {
return lhs.stops == rhs.stops;
diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp
index f6601d9ea3..9c2ad101ec 100644
--- a/include/mbgl/style/function/source_function.hpp
+++ b/include/mbgl/style/function/source_function.hpp
@@ -16,7 +16,7 @@ template <class T>
class SourceFunction {
public:
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
IntervalStops<T>,
@@ -53,6 +53,7 @@ public:
std::string property;
Stops stops;
optional<T> defaultValue;
+ bool useIntegerZoom = false;
};
} // namespace style
diff --git a/include/mbgl/style/image.hpp b/include/mbgl/style/image.hpp
index 499377467e..ff3bfedf46 100644
--- a/include/mbgl/style/image.hpp
+++ b/include/mbgl/style/image.hpp
@@ -1,24 +1,30 @@
#pragma once
#include <mbgl/util/image.hpp>
+#include <mbgl/util/immutable.hpp>
+
+#include <string>
namespace mbgl {
namespace style {
class Image {
public:
- Image(PremultipliedImage&&, float pixelRatio, bool sdf = false);
+ Image(std::string id, PremultipliedImage&&, float pixelRatio, bool sdf = false);
+ Image(const Image&);
+
+ std::string getID() const;
- PremultipliedImage image;
+ const PremultipliedImage& getImage() const;
// Pixel ratio of the sprite image.
- const float pixelRatio;
+ float getPixelRatio() const;
// Whether this image should be interpreted as a signed distance field icon.
- const bool sdf;
+ bool isSdf() const;
- float getWidth() const { return image.size.width / pixelRatio; }
- float getHeight() const { return image.size.height / pixelRatio; }
+ class Impl;
+ Immutable<Impl> baseImpl;
};
} // namespace style
diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp
index 016b3a1c8b..c6a3c0e735 100644
--- a/include/mbgl/style/layer.hpp
+++ b/include/mbgl/style/layer.hpp
@@ -2,6 +2,7 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/any.hpp>
+#include <mbgl/util/immutable.hpp>
#include <mbgl/style/layer_type.hpp>
#include <mbgl/style/types.hpp>
@@ -21,6 +22,7 @@ class RasterLayer;
class BackgroundLayer;
class CustomLayer;
class FillExtrusionLayer;
+class LayerObserver;
/**
* The runtime representation of a [layer](https://www.mapbox.com/mapbox-gl-style-spec/#layers) from the Mapbox Style
@@ -40,15 +42,6 @@ class FillExtrusionLayer;
*/
class Layer : public mbgl::util::noncopyable {
public:
- class Impl;
-
-protected:
-
- const LayerType type;
- Layer(LayerType, std::unique_ptr<Impl>);
-
-public:
-
virtual ~Layer();
// Check whether this layer is of the given subtype.
@@ -80,23 +73,23 @@ public:
//
template <class V>
auto accept(V&& visitor) {
- switch (type) {
+ switch (getType()) {
case LayerType::Fill:
- return visitor(*as<FillLayer>());
+ return std::forward<V>(visitor)(*as<FillLayer>());
case LayerType::Line:
- return visitor(*as<LineLayer>());
+ return std::forward<V>(visitor)(*as<LineLayer>());
case LayerType::Circle:
- return visitor(*as<CircleLayer>());
+ return std::forward<V>(visitor)(*as<CircleLayer>());
case LayerType::Symbol:
- return visitor(*as<SymbolLayer>());
+ return std::forward<V>(visitor)(*as<SymbolLayer>());
case LayerType::Raster:
- return visitor(*as<RasterLayer>());
+ return std::forward<V>(visitor)(*as<RasterLayer>());
case LayerType::Background:
- return visitor(*as<BackgroundLayer>());
+ return std::forward<V>(visitor)(*as<BackgroundLayer>());
case LayerType::Custom:
- return visitor(*as<CustomLayer>());
+ return std::forward<V>(visitor)(*as<CustomLayer>());
case LayerType::FillExtrusion:
- return visitor(*as<FillExtrusionLayer>());
+ return std::forward<V>(visitor)(*as<FillExtrusionLayer>());
}
@@ -105,20 +98,30 @@ public:
throw new std::runtime_error("unknown layer type");
}
- const std::string& getID() const;
+ LayerType getType() const;
+ std::string getID() const;
// Visibility
VisibilityType getVisibility() const;
- void setVisibility(VisibilityType);
+ virtual void setVisibility(VisibilityType) = 0;
// Zoom range
float getMinZoom() const;
- void setMinZoom(float) const;
float getMaxZoom() const;
- void setMaxZoom(float) const;
+ virtual void setMinZoom(float) = 0;
+ virtual void setMaxZoom(float) = 0;
// Private implementation
- const std::unique_ptr<Impl> baseImpl;
+ class Impl;
+ Immutable<Impl> baseImpl;
+
+ Layer(Immutable<Impl>);
+
+ // Create a layer, copying all properties except id and paint properties from this layer.
+ virtual std::unique_ptr<Layer> cloneRef(const std::string& id) const = 0;
+
+ LayerObserver* observer = nullptr;
+ void setObserver(LayerObserver*);
// 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
diff --git a/include/mbgl/style/layers/background_layer.hpp b/include/mbgl/style/layers/background_layer.hpp
index 6604a868f3..903983844f 100644
--- a/include/mbgl/style/layers/background_layer.hpp
+++ b/include/mbgl/style/layers/background_layer.hpp
@@ -19,38 +19,46 @@ public:
BackgroundLayer(const std::string& layerID);
~BackgroundLayer() final;
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Paint properties
static PropertyValue<Color> getDefaultBackgroundColor();
- PropertyValue<Color> getBackgroundColor(const optional<std::string>& klass = {}) const;
- void setBackgroundColor(PropertyValue<Color>, const optional<std::string>& klass = {});
- void setBackgroundColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getBackgroundColorTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<Color> getBackgroundColor() const;
+ void setBackgroundColor(PropertyValue<Color>);
+ void setBackgroundColorTransition(const TransitionOptions&);
+ TransitionOptions getBackgroundColorTransition() const;
static PropertyValue<std::string> getDefaultBackgroundPattern();
- PropertyValue<std::string> getBackgroundPattern(const optional<std::string>& klass = {}) const;
- void setBackgroundPattern(PropertyValue<std::string>, const optional<std::string>& klass = {});
- void setBackgroundPatternTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getBackgroundPatternTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::string> getBackgroundPattern() const;
+ void setBackgroundPattern(PropertyValue<std::string>);
+ void setBackgroundPatternTransition(const TransitionOptions&);
+ TransitionOptions getBackgroundPatternTransition() const;
static PropertyValue<float> getDefaultBackgroundOpacity();
- PropertyValue<float> getBackgroundOpacity(const optional<std::string>& klass = {}) const;
- void setBackgroundOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
- void setBackgroundOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getBackgroundOpacityTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getBackgroundOpacity() const;
+ void setBackgroundOpacity(PropertyValue<float>);
+ void setBackgroundOpacityTransition(const TransitionOptions&);
+ TransitionOptions getBackgroundOpacityTransition() const;
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- BackgroundLayer(const Impl&);
- BackgroundLayer(const BackgroundLayer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ BackgroundLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<BackgroundLayer>() const {
- return type == LayerType::Background;
+ return getType() == LayerType::Background;
}
} // namespace style
diff --git a/include/mbgl/style/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp
index 3a3723249f..942dd67503 100644
--- a/include/mbgl/style/layers/circle_layer.hpp
+++ b/include/mbgl/style/layers/circle_layer.hpp
@@ -27,80 +27,94 @@ public:
void setFilter(const Filter&);
const Filter& getFilter() const;
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Paint properties
static DataDrivenPropertyValue<float> getDefaultCircleRadius();
- DataDrivenPropertyValue<float> getCircleRadius(const optional<std::string>& klass = {}) const;
- void setCircleRadius(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setCircleRadiusTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleRadiusTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getCircleRadius() const;
+ void setCircleRadius(DataDrivenPropertyValue<float>);
+ void setCircleRadiusTransition(const TransitionOptions&);
+ TransitionOptions getCircleRadiusTransition() const;
static DataDrivenPropertyValue<Color> getDefaultCircleColor();
- DataDrivenPropertyValue<Color> getCircleColor(const optional<std::string>& klass = {}) const;
- void setCircleColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setCircleColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getCircleColor() const;
+ void setCircleColor(DataDrivenPropertyValue<Color>);
+ void setCircleColorTransition(const TransitionOptions&);
+ TransitionOptions getCircleColorTransition() const;
static DataDrivenPropertyValue<float> getDefaultCircleBlur();
- DataDrivenPropertyValue<float> getCircleBlur(const optional<std::string>& klass = {}) const;
- void setCircleBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setCircleBlurTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleBlurTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getCircleBlur() const;
+ void setCircleBlur(DataDrivenPropertyValue<float>);
+ void setCircleBlurTransition(const TransitionOptions&);
+ TransitionOptions getCircleBlurTransition() const;
static DataDrivenPropertyValue<float> getDefaultCircleOpacity();
- DataDrivenPropertyValue<float> getCircleOpacity(const optional<std::string>& klass = {}) const;
- void setCircleOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setCircleOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleOpacityTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getCircleOpacity() const;
+ void setCircleOpacity(DataDrivenPropertyValue<float>);
+ void setCircleOpacityTransition(const TransitionOptions&);
+ TransitionOptions getCircleOpacityTransition() const;
static PropertyValue<std::array<float, 2>> getDefaultCircleTranslate();
- PropertyValue<std::array<float, 2>> getCircleTranslate(const optional<std::string>& klass = {}) const;
- void setCircleTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
- void setCircleTranslateTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleTranslateTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::array<float, 2>> getCircleTranslate() const;
+ void setCircleTranslate(PropertyValue<std::array<float, 2>>);
+ void setCircleTranslateTransition(const TransitionOptions&);
+ TransitionOptions getCircleTranslateTransition() const;
static PropertyValue<TranslateAnchorType> getDefaultCircleTranslateAnchor();
- PropertyValue<TranslateAnchorType> getCircleTranslateAnchor(const optional<std::string>& klass = {}) const;
- void setCircleTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
- void setCircleTranslateAnchorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleTranslateAnchorTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<TranslateAnchorType> getCircleTranslateAnchor() const;
+ void setCircleTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setCircleTranslateAnchorTransition(const TransitionOptions&);
+ TransitionOptions getCircleTranslateAnchorTransition() const;
static PropertyValue<CirclePitchScaleType> getDefaultCirclePitchScale();
- PropertyValue<CirclePitchScaleType> getCirclePitchScale(const optional<std::string>& klass = {}) const;
- void setCirclePitchScale(PropertyValue<CirclePitchScaleType>, const optional<std::string>& klass = {});
- void setCirclePitchScaleTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCirclePitchScaleTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<CirclePitchScaleType> getCirclePitchScale() const;
+ void setCirclePitchScale(PropertyValue<CirclePitchScaleType>);
+ 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 optional<std::string>& klass = {}) const;
- void setCircleStrokeWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setCircleStrokeWidthTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleStrokeWidthTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getCircleStrokeWidth() const;
+ void setCircleStrokeWidth(DataDrivenPropertyValue<float>);
+ void setCircleStrokeWidthTransition(const TransitionOptions&);
+ TransitionOptions getCircleStrokeWidthTransition() const;
static DataDrivenPropertyValue<Color> getDefaultCircleStrokeColor();
- DataDrivenPropertyValue<Color> getCircleStrokeColor(const optional<std::string>& klass = {}) const;
- void setCircleStrokeColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setCircleStrokeColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleStrokeColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getCircleStrokeColor() const;
+ void setCircleStrokeColor(DataDrivenPropertyValue<Color>);
+ void setCircleStrokeColorTransition(const TransitionOptions&);
+ TransitionOptions getCircleStrokeColorTransition() const;
static DataDrivenPropertyValue<float> getDefaultCircleStrokeOpacity();
- DataDrivenPropertyValue<float> getCircleStrokeOpacity(const optional<std::string>& klass = {}) const;
- void setCircleStrokeOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setCircleStrokeOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getCircleStrokeOpacityTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getCircleStrokeOpacity() const;
+ void setCircleStrokeOpacity(DataDrivenPropertyValue<float>);
+ void setCircleStrokeOpacityTransition(const TransitionOptions&);
+ TransitionOptions getCircleStrokeOpacityTransition() const;
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- CircleLayer(const Impl&);
- CircleLayer(const CircleLayer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ CircleLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<CircleLayer>() const {
- return type == LayerType::Circle;
+ return getType() == LayerType::Circle;
}
} // namespace style
diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp
index edc8d43f89..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,16 +59,33 @@ 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
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Private implementation
class Impl;
- Impl* impl;
+ const Impl& impl() const;
+
+ Mutable<Impl> mutableImpl() const;
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
- CustomLayer(const Impl&);
CustomLayer(const CustomLayer&) = delete;
};
diff --git a/include/mbgl/style/layers/fill_extrusion_layer.hpp b/include/mbgl/style/layers/fill_extrusion_layer.hpp
index 1f79f87fac..e1c54f54ee 100644
--- a/include/mbgl/style/layers/fill_extrusion_layer.hpp
+++ b/include/mbgl/style/layers/fill_extrusion_layer.hpp
@@ -27,62 +27,70 @@ public:
void setFilter(const Filter&);
const Filter& getFilter() const;
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Paint properties
static PropertyValue<float> getDefaultFillExtrusionOpacity();
- PropertyValue<float> getFillExtrusionOpacity(const optional<std::string>& klass = {}) const;
- void setFillExtrusionOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
- void setFillExtrusionOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillExtrusionOpacityTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getFillExtrusionOpacity() const;
+ void setFillExtrusionOpacity(PropertyValue<float>);
+ void setFillExtrusionOpacityTransition(const TransitionOptions&);
+ TransitionOptions getFillExtrusionOpacityTransition() const;
static DataDrivenPropertyValue<Color> getDefaultFillExtrusionColor();
- DataDrivenPropertyValue<Color> getFillExtrusionColor(const optional<std::string>& klass = {}) const;
- void setFillExtrusionColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setFillExtrusionColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillExtrusionColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getFillExtrusionColor() const;
+ void setFillExtrusionColor(DataDrivenPropertyValue<Color>);
+ void setFillExtrusionColorTransition(const TransitionOptions&);
+ TransitionOptions getFillExtrusionColorTransition() const;
static PropertyValue<std::array<float, 2>> getDefaultFillExtrusionTranslate();
- PropertyValue<std::array<float, 2>> getFillExtrusionTranslate(const optional<std::string>& klass = {}) const;
- void setFillExtrusionTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
- void setFillExtrusionTranslateTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillExtrusionTranslateTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::array<float, 2>> getFillExtrusionTranslate() const;
+ void setFillExtrusionTranslate(PropertyValue<std::array<float, 2>>);
+ void setFillExtrusionTranslateTransition(const TransitionOptions&);
+ TransitionOptions getFillExtrusionTranslateTransition() const;
static PropertyValue<TranslateAnchorType> getDefaultFillExtrusionTranslateAnchor();
- PropertyValue<TranslateAnchorType> getFillExtrusionTranslateAnchor(const optional<std::string>& klass = {}) const;
- void setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
- void setFillExtrusionTranslateAnchorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillExtrusionTranslateAnchorTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<TranslateAnchorType> getFillExtrusionTranslateAnchor() const;
+ void setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setFillExtrusionTranslateAnchorTransition(const TransitionOptions&);
+ TransitionOptions getFillExtrusionTranslateAnchorTransition() const;
static PropertyValue<std::string> getDefaultFillExtrusionPattern();
- PropertyValue<std::string> getFillExtrusionPattern(const optional<std::string>& klass = {}) const;
- void setFillExtrusionPattern(PropertyValue<std::string>, const optional<std::string>& klass = {});
- void setFillExtrusionPatternTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillExtrusionPatternTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::string> getFillExtrusionPattern() const;
+ void setFillExtrusionPattern(PropertyValue<std::string>);
+ void setFillExtrusionPatternTransition(const TransitionOptions&);
+ TransitionOptions getFillExtrusionPatternTransition() const;
static DataDrivenPropertyValue<float> getDefaultFillExtrusionHeight();
- DataDrivenPropertyValue<float> getFillExtrusionHeight(const optional<std::string>& klass = {}) const;
- void setFillExtrusionHeight(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setFillExtrusionHeightTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillExtrusionHeightTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getFillExtrusionHeight() const;
+ void setFillExtrusionHeight(DataDrivenPropertyValue<float>);
+ void setFillExtrusionHeightTransition(const TransitionOptions&);
+ TransitionOptions getFillExtrusionHeightTransition() const;
static DataDrivenPropertyValue<float> getDefaultFillExtrusionBase();
- DataDrivenPropertyValue<float> getFillExtrusionBase(const optional<std::string>& klass = {}) const;
- void setFillExtrusionBase(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setFillExtrusionBaseTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillExtrusionBaseTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getFillExtrusionBase() const;
+ void setFillExtrusionBase(DataDrivenPropertyValue<float>);
+ void setFillExtrusionBaseTransition(const TransitionOptions&);
+ TransitionOptions getFillExtrusionBaseTransition() const;
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- FillExtrusionLayer(const Impl&);
- FillExtrusionLayer(const FillExtrusionLayer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ FillExtrusionLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<FillExtrusionLayer>() const {
- return type == LayerType::FillExtrusion;
+ return getType() == LayerType::FillExtrusion;
}
} // namespace style
diff --git a/include/mbgl/style/layers/fill_layer.hpp b/include/mbgl/style/layers/fill_layer.hpp
index 8371ff7a8f..dfbe69d7fe 100644
--- a/include/mbgl/style/layers/fill_layer.hpp
+++ b/include/mbgl/style/layers/fill_layer.hpp
@@ -27,62 +27,70 @@ public:
void setFilter(const Filter&);
const Filter& getFilter() const;
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Paint properties
static PropertyValue<bool> getDefaultFillAntialias();
- PropertyValue<bool> getFillAntialias(const optional<std::string>& klass = {}) const;
- void setFillAntialias(PropertyValue<bool>, const optional<std::string>& klass = {});
- void setFillAntialiasTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillAntialiasTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<bool> getFillAntialias() const;
+ void setFillAntialias(PropertyValue<bool>);
+ void setFillAntialiasTransition(const TransitionOptions&);
+ TransitionOptions getFillAntialiasTransition() const;
static DataDrivenPropertyValue<float> getDefaultFillOpacity();
- DataDrivenPropertyValue<float> getFillOpacity(const optional<std::string>& klass = {}) const;
- void setFillOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setFillOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillOpacityTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getFillOpacity() const;
+ void setFillOpacity(DataDrivenPropertyValue<float>);
+ void setFillOpacityTransition(const TransitionOptions&);
+ TransitionOptions getFillOpacityTransition() const;
static DataDrivenPropertyValue<Color> getDefaultFillColor();
- DataDrivenPropertyValue<Color> getFillColor(const optional<std::string>& klass = {}) const;
- void setFillColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setFillColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getFillColor() const;
+ void setFillColor(DataDrivenPropertyValue<Color>);
+ void setFillColorTransition(const TransitionOptions&);
+ TransitionOptions getFillColorTransition() const;
static DataDrivenPropertyValue<Color> getDefaultFillOutlineColor();
- DataDrivenPropertyValue<Color> getFillOutlineColor(const optional<std::string>& klass = {}) const;
- void setFillOutlineColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setFillOutlineColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillOutlineColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getFillOutlineColor() const;
+ void setFillOutlineColor(DataDrivenPropertyValue<Color>);
+ void setFillOutlineColorTransition(const TransitionOptions&);
+ TransitionOptions getFillOutlineColorTransition() const;
static PropertyValue<std::array<float, 2>> getDefaultFillTranslate();
- PropertyValue<std::array<float, 2>> getFillTranslate(const optional<std::string>& klass = {}) const;
- void setFillTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
- void setFillTranslateTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillTranslateTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::array<float, 2>> getFillTranslate() const;
+ void setFillTranslate(PropertyValue<std::array<float, 2>>);
+ void setFillTranslateTransition(const TransitionOptions&);
+ TransitionOptions getFillTranslateTransition() const;
static PropertyValue<TranslateAnchorType> getDefaultFillTranslateAnchor();
- PropertyValue<TranslateAnchorType> getFillTranslateAnchor(const optional<std::string>& klass = {}) const;
- void setFillTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
- void setFillTranslateAnchorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillTranslateAnchorTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<TranslateAnchorType> getFillTranslateAnchor() const;
+ void setFillTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setFillTranslateAnchorTransition(const TransitionOptions&);
+ TransitionOptions getFillTranslateAnchorTransition() const;
static PropertyValue<std::string> getDefaultFillPattern();
- PropertyValue<std::string> getFillPattern(const optional<std::string>& klass = {}) const;
- void setFillPattern(PropertyValue<std::string>, const optional<std::string>& klass = {});
- void setFillPatternTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getFillPatternTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::string> getFillPattern() const;
+ void setFillPattern(PropertyValue<std::string>);
+ void setFillPatternTransition(const TransitionOptions&);
+ TransitionOptions getFillPatternTransition() const;
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- FillLayer(const Impl&);
- FillLayer(const FillLayer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ FillLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<FillLayer>() const {
- return type == LayerType::Fill;
+ return getType() == LayerType::Fill;
}
} // namespace style
diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs
index 59d7cd6415..4ee5545247 100644
--- a/include/mbgl/style/layers/layer.hpp.ejs
+++ b/include/mbgl/style/layers/layer.hpp.ejs
@@ -44,6 +44,13 @@ public:
<% } -%>
<% } -%>
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
<% if (layoutProperties.length) { -%>
// Layout properties
@@ -58,24 +65,25 @@ public:
<% for (const property of paintProperties) { -%>
static <%- propertyValueType(property) %> getDefault<%- camelize(property.name) %>();
- <%- propertyValueType(property) %> get<%- camelize(property.name) %>(const optional<std::string>& klass = {}) const;
- void set<%- camelize(property.name) %>(<%- propertyValueType(property) %>, const optional<std::string>& klass = {});
- void set<%- camelize(property.name) %>Transition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions get<%- camelize(property.name) %>Transition(const optional<std::string>& klass = {}) const;
+ <%- propertyValueType(property) %> get<%- camelize(property.name) %>() const;
+ void set<%- camelize(property.name) %>(<%- propertyValueType(property) %>);
+ void set<%- camelize(property.name) %>Transition(const TransitionOptions&);
+ TransitionOptions get<%- camelize(property.name) %>Transition() const;
<% } -%>
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- <%- camelize(type) %>Layer(const Impl&);
- <%- camelize(type) %>Layer(const <%- camelize(type) %>Layer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ <%- camelize(type) %>Layer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<<%- camelize(type) %>Layer>() const {
- return type == LayerType::<%- camelize(type) %>;
+ return getType() == LayerType::<%- camelize(type) %>;
}
} // namespace style
diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp
index a5f08e553c..4519296323 100644
--- a/include/mbgl/style/layers/line_layer.hpp
+++ b/include/mbgl/style/layers/line_layer.hpp
@@ -29,15 +29,22 @@ public:
void setFilter(const Filter&);
const Filter& getFilter() const;
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Layout properties
static PropertyValue<LineCapType> getDefaultLineCap();
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;
@@ -50,77 +57,78 @@ public:
// Paint properties
static DataDrivenPropertyValue<float> getDefaultLineOpacity();
- DataDrivenPropertyValue<float> getLineOpacity(const optional<std::string>& klass = {}) const;
- void setLineOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setLineOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineOpacityTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getLineOpacity() const;
+ void setLineOpacity(DataDrivenPropertyValue<float>);
+ void setLineOpacityTransition(const TransitionOptions&);
+ TransitionOptions getLineOpacityTransition() const;
static DataDrivenPropertyValue<Color> getDefaultLineColor();
- DataDrivenPropertyValue<Color> getLineColor(const optional<std::string>& klass = {}) const;
- void setLineColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setLineColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getLineColor() const;
+ void setLineColor(DataDrivenPropertyValue<Color>);
+ void setLineColorTransition(const TransitionOptions&);
+ TransitionOptions getLineColorTransition() const;
static PropertyValue<std::array<float, 2>> getDefaultLineTranslate();
- PropertyValue<std::array<float, 2>> getLineTranslate(const optional<std::string>& klass = {}) const;
- void setLineTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
- void setLineTranslateTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineTranslateTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::array<float, 2>> getLineTranslate() const;
+ void setLineTranslate(PropertyValue<std::array<float, 2>>);
+ void setLineTranslateTransition(const TransitionOptions&);
+ TransitionOptions getLineTranslateTransition() const;
static PropertyValue<TranslateAnchorType> getDefaultLineTranslateAnchor();
- PropertyValue<TranslateAnchorType> getLineTranslateAnchor(const optional<std::string>& klass = {}) const;
- void setLineTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
- void setLineTranslateAnchorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineTranslateAnchorTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<TranslateAnchorType> getLineTranslateAnchor() const;
+ void setLineTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setLineTranslateAnchorTransition(const TransitionOptions&);
+ TransitionOptions getLineTranslateAnchorTransition() const;
- static PropertyValue<float> getDefaultLineWidth();
- PropertyValue<float> getLineWidth(const optional<std::string>& klass = {}) const;
- void setLineWidth(PropertyValue<float>, const optional<std::string>& klass = {});
- void setLineWidthTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineWidthTransition(const optional<std::string>& klass = {}) const;
+ static DataDrivenPropertyValue<float> getDefaultLineWidth();
+ DataDrivenPropertyValue<float> getLineWidth() const;
+ void setLineWidth(DataDrivenPropertyValue<float>);
+ void setLineWidthTransition(const TransitionOptions&);
+ TransitionOptions getLineWidthTransition() const;
static DataDrivenPropertyValue<float> getDefaultLineGapWidth();
- DataDrivenPropertyValue<float> getLineGapWidth(const optional<std::string>& klass = {}) const;
- void setLineGapWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setLineGapWidthTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineGapWidthTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getLineGapWidth() const;
+ void setLineGapWidth(DataDrivenPropertyValue<float>);
+ void setLineGapWidthTransition(const TransitionOptions&);
+ TransitionOptions getLineGapWidthTransition() const;
static DataDrivenPropertyValue<float> getDefaultLineOffset();
- DataDrivenPropertyValue<float> getLineOffset(const optional<std::string>& klass = {}) const;
- void setLineOffset(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setLineOffsetTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineOffsetTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getLineOffset() const;
+ void setLineOffset(DataDrivenPropertyValue<float>);
+ void setLineOffsetTransition(const TransitionOptions&);
+ TransitionOptions getLineOffsetTransition() const;
static DataDrivenPropertyValue<float> getDefaultLineBlur();
- DataDrivenPropertyValue<float> getLineBlur(const optional<std::string>& klass = {}) const;
- void setLineBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setLineBlurTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineBlurTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getLineBlur() const;
+ void setLineBlur(DataDrivenPropertyValue<float>);
+ void setLineBlurTransition(const TransitionOptions&);
+ TransitionOptions getLineBlurTransition() const;
static PropertyValue<std::vector<float>> getDefaultLineDasharray();
- PropertyValue<std::vector<float>> getLineDasharray(const optional<std::string>& klass = {}) const;
- void setLineDasharray(PropertyValue<std::vector<float>>, const optional<std::string>& klass = {});
- void setLineDasharrayTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLineDasharrayTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::vector<float>> getLineDasharray() const;
+ void setLineDasharray(PropertyValue<std::vector<float>>);
+ void setLineDasharrayTransition(const TransitionOptions&);
+ TransitionOptions getLineDasharrayTransition() const;
static PropertyValue<std::string> getDefaultLinePattern();
- PropertyValue<std::string> getLinePattern(const optional<std::string>& klass = {}) const;
- void setLinePattern(PropertyValue<std::string>, const optional<std::string>& klass = {});
- void setLinePatternTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getLinePatternTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::string> getLinePattern() const;
+ void setLinePattern(PropertyValue<std::string>);
+ void setLinePatternTransition(const TransitionOptions&);
+ TransitionOptions getLinePatternTransition() const;
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- LineLayer(const Impl&);
- LineLayer(const LineLayer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ LineLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<LineLayer>() const {
- return type == LayerType::Line;
+ return getType() == LayerType::Line;
}
} // namespace style
diff --git a/include/mbgl/style/layers/raster_layer.hpp b/include/mbgl/style/layers/raster_layer.hpp
index c0351da5d0..8111364709 100644
--- a/include/mbgl/style/layers/raster_layer.hpp
+++ b/include/mbgl/style/layers/raster_layer.hpp
@@ -22,62 +22,70 @@ public:
// Source
const std::string& getSourceID() const;
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Paint properties
static PropertyValue<float> getDefaultRasterOpacity();
- PropertyValue<float> getRasterOpacity(const optional<std::string>& klass = {}) const;
- void setRasterOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
- void setRasterOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getRasterOpacityTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getRasterOpacity() const;
+ void setRasterOpacity(PropertyValue<float>);
+ void setRasterOpacityTransition(const TransitionOptions&);
+ TransitionOptions getRasterOpacityTransition() const;
static PropertyValue<float> getDefaultRasterHueRotate();
- PropertyValue<float> getRasterHueRotate(const optional<std::string>& klass = {}) const;
- void setRasterHueRotate(PropertyValue<float>, const optional<std::string>& klass = {});
- void setRasterHueRotateTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getRasterHueRotateTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getRasterHueRotate() const;
+ void setRasterHueRotate(PropertyValue<float>);
+ void setRasterHueRotateTransition(const TransitionOptions&);
+ TransitionOptions getRasterHueRotateTransition() const;
static PropertyValue<float> getDefaultRasterBrightnessMin();
- PropertyValue<float> getRasterBrightnessMin(const optional<std::string>& klass = {}) const;
- void setRasterBrightnessMin(PropertyValue<float>, const optional<std::string>& klass = {});
- void setRasterBrightnessMinTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getRasterBrightnessMinTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getRasterBrightnessMin() const;
+ void setRasterBrightnessMin(PropertyValue<float>);
+ void setRasterBrightnessMinTransition(const TransitionOptions&);
+ TransitionOptions getRasterBrightnessMinTransition() const;
static PropertyValue<float> getDefaultRasterBrightnessMax();
- PropertyValue<float> getRasterBrightnessMax(const optional<std::string>& klass = {}) const;
- void setRasterBrightnessMax(PropertyValue<float>, const optional<std::string>& klass = {});
- void setRasterBrightnessMaxTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getRasterBrightnessMaxTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getRasterBrightnessMax() const;
+ void setRasterBrightnessMax(PropertyValue<float>);
+ void setRasterBrightnessMaxTransition(const TransitionOptions&);
+ TransitionOptions getRasterBrightnessMaxTransition() const;
static PropertyValue<float> getDefaultRasterSaturation();
- PropertyValue<float> getRasterSaturation(const optional<std::string>& klass = {}) const;
- void setRasterSaturation(PropertyValue<float>, const optional<std::string>& klass = {});
- void setRasterSaturationTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getRasterSaturationTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getRasterSaturation() const;
+ void setRasterSaturation(PropertyValue<float>);
+ void setRasterSaturationTransition(const TransitionOptions&);
+ TransitionOptions getRasterSaturationTransition() const;
static PropertyValue<float> getDefaultRasterContrast();
- PropertyValue<float> getRasterContrast(const optional<std::string>& klass = {}) const;
- void setRasterContrast(PropertyValue<float>, const optional<std::string>& klass = {});
- void setRasterContrastTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getRasterContrastTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getRasterContrast() const;
+ void setRasterContrast(PropertyValue<float>);
+ void setRasterContrastTransition(const TransitionOptions&);
+ TransitionOptions getRasterContrastTransition() const;
static PropertyValue<float> getDefaultRasterFadeDuration();
- PropertyValue<float> getRasterFadeDuration(const optional<std::string>& klass = {}) const;
- void setRasterFadeDuration(PropertyValue<float>, const optional<std::string>& klass = {});
- void setRasterFadeDurationTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getRasterFadeDurationTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<float> getRasterFadeDuration() const;
+ void setRasterFadeDuration(PropertyValue<float>);
+ void setRasterFadeDurationTransition(const TransitionOptions&);
+ TransitionOptions getRasterFadeDurationTransition() const;
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- RasterLayer(const Impl&);
- RasterLayer(const RasterLayer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ RasterLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<RasterLayer>() const {
- return type == LayerType::Raster;
+ return getType() == LayerType::Raster;
}
} // namespace style
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index ea6bda55d7..a72baa0b4e 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -29,6 +29,13 @@ public:
void setFilter(const Filter&);
const Filter& getFilter() const;
+ // Visibility
+ void setVisibility(VisibilityType) final;
+
+ // Zoom range
+ void setMinZoom(float) final;
+ void setMaxZoom(float) final;
+
// Layout properties
static PropertyValue<SymbolPlacementType> getDefaultSymbolPlacement();
@@ -91,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>);
@@ -111,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;
@@ -170,101 +185,102 @@ public:
// Paint properties
static DataDrivenPropertyValue<float> getDefaultIconOpacity();
- DataDrivenPropertyValue<float> getIconOpacity(const optional<std::string>& klass = {}) const;
- void setIconOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setIconOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getIconOpacityTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getIconOpacity() const;
+ void setIconOpacity(DataDrivenPropertyValue<float>);
+ void setIconOpacityTransition(const TransitionOptions&);
+ TransitionOptions getIconOpacityTransition() const;
static DataDrivenPropertyValue<Color> getDefaultIconColor();
- DataDrivenPropertyValue<Color> getIconColor(const optional<std::string>& klass = {}) const;
- void setIconColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setIconColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getIconColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getIconColor() const;
+ void setIconColor(DataDrivenPropertyValue<Color>);
+ void setIconColorTransition(const TransitionOptions&);
+ TransitionOptions getIconColorTransition() const;
static DataDrivenPropertyValue<Color> getDefaultIconHaloColor();
- DataDrivenPropertyValue<Color> getIconHaloColor(const optional<std::string>& klass = {}) const;
- void setIconHaloColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setIconHaloColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getIconHaloColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getIconHaloColor() const;
+ void setIconHaloColor(DataDrivenPropertyValue<Color>);
+ void setIconHaloColorTransition(const TransitionOptions&);
+ TransitionOptions getIconHaloColorTransition() const;
static DataDrivenPropertyValue<float> getDefaultIconHaloWidth();
- DataDrivenPropertyValue<float> getIconHaloWidth(const optional<std::string>& klass = {}) const;
- void setIconHaloWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setIconHaloWidthTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getIconHaloWidthTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getIconHaloWidth() const;
+ void setIconHaloWidth(DataDrivenPropertyValue<float>);
+ void setIconHaloWidthTransition(const TransitionOptions&);
+ TransitionOptions getIconHaloWidthTransition() const;
static DataDrivenPropertyValue<float> getDefaultIconHaloBlur();
- DataDrivenPropertyValue<float> getIconHaloBlur(const optional<std::string>& klass = {}) const;
- void setIconHaloBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setIconHaloBlurTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getIconHaloBlurTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getIconHaloBlur() const;
+ void setIconHaloBlur(DataDrivenPropertyValue<float>);
+ void setIconHaloBlurTransition(const TransitionOptions&);
+ TransitionOptions getIconHaloBlurTransition() const;
static PropertyValue<std::array<float, 2>> getDefaultIconTranslate();
- PropertyValue<std::array<float, 2>> getIconTranslate(const optional<std::string>& klass = {}) const;
- void setIconTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
- void setIconTranslateTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getIconTranslateTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::array<float, 2>> getIconTranslate() const;
+ void setIconTranslate(PropertyValue<std::array<float, 2>>);
+ void setIconTranslateTransition(const TransitionOptions&);
+ TransitionOptions getIconTranslateTransition() const;
static PropertyValue<TranslateAnchorType> getDefaultIconTranslateAnchor();
- PropertyValue<TranslateAnchorType> getIconTranslateAnchor(const optional<std::string>& klass = {}) const;
- void setIconTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
- void setIconTranslateAnchorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getIconTranslateAnchorTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<TranslateAnchorType> getIconTranslateAnchor() const;
+ void setIconTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setIconTranslateAnchorTransition(const TransitionOptions&);
+ TransitionOptions getIconTranslateAnchorTransition() const;
static DataDrivenPropertyValue<float> getDefaultTextOpacity();
- DataDrivenPropertyValue<float> getTextOpacity(const optional<std::string>& klass = {}) const;
- void setTextOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setTextOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getTextOpacityTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getTextOpacity() const;
+ void setTextOpacity(DataDrivenPropertyValue<float>);
+ void setTextOpacityTransition(const TransitionOptions&);
+ TransitionOptions getTextOpacityTransition() const;
static DataDrivenPropertyValue<Color> getDefaultTextColor();
- DataDrivenPropertyValue<Color> getTextColor(const optional<std::string>& klass = {}) const;
- void setTextColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setTextColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getTextColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getTextColor() const;
+ void setTextColor(DataDrivenPropertyValue<Color>);
+ void setTextColorTransition(const TransitionOptions&);
+ TransitionOptions getTextColorTransition() const;
static DataDrivenPropertyValue<Color> getDefaultTextHaloColor();
- DataDrivenPropertyValue<Color> getTextHaloColor(const optional<std::string>& klass = {}) const;
- void setTextHaloColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {});
- void setTextHaloColorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getTextHaloColorTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<Color> getTextHaloColor() const;
+ void setTextHaloColor(DataDrivenPropertyValue<Color>);
+ void setTextHaloColorTransition(const TransitionOptions&);
+ TransitionOptions getTextHaloColorTransition() const;
static DataDrivenPropertyValue<float> getDefaultTextHaloWidth();
- DataDrivenPropertyValue<float> getTextHaloWidth(const optional<std::string>& klass = {}) const;
- void setTextHaloWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setTextHaloWidthTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getTextHaloWidthTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getTextHaloWidth() const;
+ void setTextHaloWidth(DataDrivenPropertyValue<float>);
+ void setTextHaloWidthTransition(const TransitionOptions&);
+ TransitionOptions getTextHaloWidthTransition() const;
static DataDrivenPropertyValue<float> getDefaultTextHaloBlur();
- DataDrivenPropertyValue<float> getTextHaloBlur(const optional<std::string>& klass = {}) const;
- void setTextHaloBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {});
- void setTextHaloBlurTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getTextHaloBlurTransition(const optional<std::string>& klass = {}) const;
+ DataDrivenPropertyValue<float> getTextHaloBlur() const;
+ void setTextHaloBlur(DataDrivenPropertyValue<float>);
+ void setTextHaloBlurTransition(const TransitionOptions&);
+ TransitionOptions getTextHaloBlurTransition() const;
static PropertyValue<std::array<float, 2>> getDefaultTextTranslate();
- PropertyValue<std::array<float, 2>> getTextTranslate(const optional<std::string>& klass = {}) const;
- void setTextTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
- void setTextTranslateTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getTextTranslateTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<std::array<float, 2>> getTextTranslate() const;
+ void setTextTranslate(PropertyValue<std::array<float, 2>>);
+ void setTextTranslateTransition(const TransitionOptions&);
+ TransitionOptions getTextTranslateTransition() const;
static PropertyValue<TranslateAnchorType> getDefaultTextTranslateAnchor();
- PropertyValue<TranslateAnchorType> getTextTranslateAnchor(const optional<std::string>& klass = {}) const;
- void setTextTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
- void setTextTranslateAnchorTransition(const TransitionOptions&, const optional<std::string>& klass = {});
- TransitionOptions getTextTranslateAnchorTransition(const optional<std::string>& klass = {}) const;
+ PropertyValue<TranslateAnchorType> getTextTranslateAnchor() const;
+ void setTextTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setTextTranslateAnchorTransition(const TransitionOptions&);
+ TransitionOptions getTextTranslateAnchorTransition() const;
// Private implementation
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
- SymbolLayer(const Impl&);
- SymbolLayer(const SymbolLayer&) = delete;
+ Mutable<Impl> mutableImpl() const;
+ SymbolLayer(Immutable<Impl>);
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
};
template <>
inline bool Layer::is<SymbolLayer>() const {
- return type == LayerType::Symbol;
+ return getType() == LayerType::Symbol;
}
} // namespace style
diff --git a/include/mbgl/style/light.hpp b/include/mbgl/style/light.hpp
index 8212a58dcc..c82792b28d 100644
--- a/include/mbgl/style/light.hpp
+++ b/include/mbgl/style/light.hpp
@@ -1,20 +1,19 @@
// This file is generated. Do not edit.
#pragma once
+
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/types.hpp>
-
-#include <memory>
+#include <mbgl/util/immutable.hpp>
namespace mbgl {
namespace style {
+class LightObserver;
+
class Light {
public:
-
- class Impl;
-
Light();
~Light();
@@ -42,7 +41,12 @@ public:
void setIntensityTransition(const TransitionOptions&);
TransitionOptions getIntensityTransition() const;
- std::shared_ptr<Impl> impl;
+ class Impl;
+ Immutable<Impl> impl;
+ Mutable<Impl> mutableImpl() const;
+
+ LightObserver* observer = nullptr;
+ void setObserver(LightObserver*);
};
} // namespace style
diff --git a/include/mbgl/style/light.hpp.ejs b/include/mbgl/style/light.hpp.ejs
index 601e0bd410..adc5b651e3 100644
--- a/include/mbgl/style/light.hpp.ejs
+++ b/include/mbgl/style/light.hpp.ejs
@@ -4,20 +4,19 @@
// This file is generated. Do not edit.
#pragma once
+
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/types.hpp>
-
-#include <memory>
+#include <mbgl/util/immutable.hpp>
namespace mbgl {
namespace style {
+class LightObserver;
+
class Light {
public:
-
- class Impl;
-
Light();
~Light();
@@ -29,7 +28,12 @@ public:
TransitionOptions get<%- camelize(property.name) %>Transition() const;
<% } -%>
- std::shared_ptr<Impl> impl;
+ class Impl;
+ Immutable<Impl> impl;
+ Mutable<Impl> mutableImpl() const;
+
+ LightObserver* observer = nullptr;
+ void setObserver(LightObserver*);
};
} // namespace style
diff --git a/include/mbgl/style/position.hpp b/include/mbgl/style/position.hpp
index 078e62bda8..3be8d1c55e 100644
--- a/include/mbgl/style/position.hpp
+++ b/include/mbgl/style/position.hpp
@@ -9,7 +9,7 @@ namespace style {
class Position {
public:
Position() = default;
- Position(const std::array<float, 3>& position_)
+ Position(std::array<float, 3>& position_)
: radial(position_[0]), azimuthal(position_[1]), polar(position_[2]) {
calculateCartesian();
};
diff --git a/include/mbgl/style/property_value.hpp b/include/mbgl/style/property_value.hpp
index 8bec7388e5..02d3a31148 100644
--- a/include/mbgl/style/property_value.hpp
+++ b/include/mbgl/style/property_value.hpp
@@ -35,9 +35,13 @@ public:
const CameraFunction<T>& asCameraFunction() const { return value.template get<CameraFunction<T>>(); }
template <typename Evaluator>
- auto evaluate(const Evaluator& evaluator) const {
+ auto evaluate(const Evaluator& evaluator, TimePoint = {}) const {
return Value::visit(value, evaluator);
}
+
+ bool hasDataDrivenPropertyDifference(const PropertyValue<T>&) const {
+ return false;
+ }
};
} // namespace style
diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp
index 4c82e472a6..cec9619451 100644
--- a/include/mbgl/style/source.hpp
+++ b/include/mbgl/style/source.hpp
@@ -1,19 +1,25 @@
#pragma once
-#include <mbgl/util/feature.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/range.hpp>
#include <mbgl/util/any.hpp>
+#include <mbgl/util/immutable.hpp>
#include <mbgl/style/types.hpp>
#include <memory>
#include <string>
-#include <vector>
namespace mbgl {
+
+class FileSource;
+
namespace style {
+class VectorSource;
+class RasterSource;
+class GeoJSONSource;
+class SourceObserver;
+
/**
* The runtime representation of a [source](https://www.mapbox.com/mapbox-gl-style-spec/#sources) from the Mapbox Style
* Specification.
@@ -49,21 +55,28 @@ public:
return is<T>() ? reinterpret_cast<const T*>(this) : nullptr;
}
- const std::string& getID() const;
+ SourceType getType() const;
+ std::string getID() const;
optional<std::string> getAttribution() const;
// Private implementation
class Impl;
- const std::unique_ptr<Impl> baseImpl;
+ Immutable<Impl> baseImpl;
+
+ Source(Immutable<Impl>);
+
+ void setObserver(SourceObserver*);
+ SourceObserver* observer = nullptr;
+
+ virtual void loadDescription(FileSource&) = 0;
+ void dumpDebugLogs() const;
+
+ bool loaded = false;
// 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;
-
-protected:
- const SourceType type;
- Source(SourceType, std::unique_ptr<Impl>);
};
} // namespace style
diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp
index 5b39d7821b..2dcfec51aa 100644
--- a/include/mbgl/style/sources/geojson_source.hpp
+++ b/include/mbgl/style/sources/geojson_source.hpp
@@ -5,6 +5,9 @@
#include <mbgl/util/optional.hpp>
namespace mbgl {
+
+class AsyncRequest;
+
namespace style {
struct GeoJSONOptions {
@@ -22,21 +25,26 @@ struct GeoJSONOptions {
class GeoJSONSource : public Source {
public:
GeoJSONSource(const std::string& id, const GeoJSONOptions& = {});
+ ~GeoJSONSource() final;
void setURL(const std::string& url);
void setGeoJSON(const GeoJSON&);
optional<std::string> getURL() const;
- // Private implementation
-
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
+
+ void loadDescription(FileSource&) final;
+
+private:
+ optional<std::string> url;
+ std::unique_ptr<AsyncRequest> req;
};
template <>
inline bool Source::is<GeoJSONSource>() const {
- return type == SourceType::GeoJSON;
+ return getType() == SourceType::GeoJSON;
}
} // namespace style
diff --git a/include/mbgl/style/sources/image_source.hpp b/include/mbgl/style/sources/image_source.hpp
new file mode 100644
index 0000000000..009764291f
--- /dev/null
+++ b/include/mbgl/style/sources/image_source.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+class LatLng;
+class AsyncRequest;
+
+namespace style {
+
+class ImageSource : public Source {
+public:
+ ImageSource(std::string id, const std::array<LatLng, 4>);
+ ~ImageSource() override;
+
+ optional<std::string> getURL() const;
+ void setURL(const std::string& url);
+
+ void setImage(PremultipliedImage&&);
+
+ void setCoordinates(const std::array<LatLng, 4>&);
+ std::array<LatLng, 4> getCoordinates() const;
+
+ class Impl;
+ const Impl& impl() const;
+
+ void loadDescription(FileSource&) final;
+private:
+ optional<std::string> url;
+ std::unique_ptr<AsyncRequest> req;
+};
+
+template <>
+inline bool Source::is<ImageSource>() const {
+ return getType() == SourceType::Image;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp
index 395f25e51d..7f23a7ca4b 100644
--- a/include/mbgl/style/sources/raster_source.hpp
+++ b/include/mbgl/style/sources/raster_source.hpp
@@ -5,23 +5,34 @@
#include <mbgl/util/variant.hpp>
namespace mbgl {
+
+class AsyncRequest;
+
namespace style {
class RasterSource : public Source {
public:
RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize);
+ ~RasterSource() final;
+ const variant<std::string, Tileset>& getURLOrTileset() const;
optional<std::string> getURL() const;
- // Private implementation
+ uint16_t getTileSize() const;
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
+
+ void loadDescription(FileSource&) final;
+
+private:
+ const variant<std::string, Tileset> urlOrTileset;
+ std::unique_ptr<AsyncRequest> req;
};
template <>
inline bool Source::is<RasterSource>() const {
- return type == SourceType::Raster;
+ return getType() == SourceType::Raster;
}
} // namespace style
diff --git a/include/mbgl/style/sources/vector_source.hpp b/include/mbgl/style/sources/vector_source.hpp
index 8626ce160a..6f16974b40 100644
--- a/include/mbgl/style/sources/vector_source.hpp
+++ b/include/mbgl/style/sources/vector_source.hpp
@@ -5,23 +5,32 @@
#include <mbgl/util/variant.hpp>
namespace mbgl {
+
+class AsyncRequest;
+
namespace style {
class VectorSource : public Source {
public:
VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset);
+ ~VectorSource() final;
+ const variant<std::string, Tileset>& getURLOrTileset() const;
optional<std::string> getURL() const;
- // Private implementation
-
class Impl;
- Impl* const impl;
+ const Impl& impl() const;
+
+ void loadDescription(FileSource&) final;
+
+private:
+ const variant<std::string, Tileset> urlOrTileset;
+ std::unique_ptr<AsyncRequest> req;
};
template <>
inline bool Source::is<VectorSource>() const {
- return type == SourceType::Vector;
+ return getType() == SourceType::Vector;
}
} // namespace style
diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp
new file mode 100644
index 0000000000..d6fdbd8f2c
--- /dev/null
+++ b/include/mbgl/style/style.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/map/camera.hpp>
+#include <mbgl/util/geo.hpp>
+
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace mbgl {
+
+class FileSource;
+class Scheduler;
+
+namespace style {
+
+class Light;
+class Image;
+class Source;
+class Layer;
+
+class Style {
+public:
+ Style(Scheduler&, FileSource&, float pixelRatio);
+ ~Style();
+
+ void loadJSON(const std::string&);
+ void loadURL(const std::string&);
+
+ std::string getJSON() const;
+ std::string getURL() const;
+
+ // Defaults
+ std::string getName() const;
+ CameraOptions getDefaultCamera() const;
+
+ // TransitionOptions
+ TransitionOptions getTransitionOptions() const;
+ void setTransitionOptions(const TransitionOptions&);
+
+ // Light
+ Light* getLight();
+ const Light* getLight() const;
+
+ void setLight(std::unique_ptr<Light>);
+
+ // Images
+ const Image* getImage(const std::string&) const;
+ void addImage(std::unique_ptr<Image>);
+ void removeImage(const std::string&);
+
+ // Sources
+ std::vector< Source*> getSources();
+ std::vector<const Source*> getSources() const;
+
+ Source* getSource(const std::string&);
+ const Source* getSource(const std::string&) const;
+
+ void addSource(std::unique_ptr<Source>);
+ std::unique_ptr<Source> removeSource(const std::string& sourceID);
+
+ // Layers
+ std::vector< Layer*> getLayers();
+ std::vector<const Layer*> getLayers() const;
+
+ Layer* getLayer(const std::string&);
+ const Layer* getLayer(const std::string&) const;
+
+ void addLayer(std::unique_ptr<Layer>, const optional<std::string>& beforeLayerID = {});
+ std::unique_ptr<Layer> removeLayer(const std::string& layerID);
+
+ // Private implementation
+ class Impl;
+ const std::unique_ptr<Impl> impl;
+};
+
+} // namespace style
+} // namespace mbgl
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 e0436efb67..ec7358de8c 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -10,7 +10,8 @@ enum class SourceType : uint8_t {
Raster,
GeoJSON,
Video,
- Annotations
+ Annotations,
+ Image
};
namespace style {
@@ -67,7 +68,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..0457dd3a07 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 {
@@ -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/util/constants.hpp b/include/mbgl/util/constants.hpp
index dbc4bddde5..d5e55065c4 100644
--- a/include/mbgl/util/constants.hpp
+++ b/include/mbgl/util/constants.hpp
@@ -38,10 +38,13 @@ 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;
-constexpr Duration DEFAULT_FADE_DURATION = Milliseconds(300);
+constexpr Duration DEFAULT_TRANSITION_DURATION = Milliseconds(300);
constexpr Seconds CLOCK_SKEW_RETRY_TIMEOUT { 30 };
constexpr UnitBezier DEFAULT_TRANSITION_EASE = { 0, 0, 0.25, 1 };
@@ -58,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/image.hpp b/include/mbgl/util/image.hpp
index a41b8462bd..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; }
@@ -78,10 +79,52 @@ public:
std::fill(data.get(), data.get() + bytes(), value);
}
+ void resize(Size size_) {
+ if (size == size_) {
+ return;
+ }
+ Image newImage(size_);
+ newImage.fill(0);
+ copy(*this, newImage, {0, 0}, {0, 0}, {
+ std::min(size.width, size_.width),
+ std::min(size.height, size_.height)
+ });
+ 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.
static void copy(const Image& srcImg, Image& dstImg, const Point<uint32_t>& srcPt, const Point<uint32_t>& dstPt, const Size& size) {
+ if (size.isEmpty()) {
+ return;
+ }
+
if (!srcImg.valid()) {
throw std::invalid_argument("invalid source for image copy");
}
diff --git a/include/mbgl/util/immutable.hpp b/include/mbgl/util/immutable.hpp
new file mode 100644
index 0000000000..eb26c0d282
--- /dev/null
+++ b/include/mbgl/util/immutable.hpp
@@ -0,0 +1,133 @@
+#pragma once
+
+#include <memory>
+
+namespace mbgl {
+
+/**
+ * `Mutable<T>` is a non-nullable uniquely owning reference to a `T`. It can be efficiently converted
+ * to `Immutable<T>`.
+ *
+ * The lifecycle of `Mutable<T>` and `Immutable<T>` is as follows:
+ *
+ * 1. Create a `Mutable<T>` using `makeMutable(...)`
+ * 2. Mutate it freely
+ * 3. When you're ready to freeze its state and enable safe cross-thread sharing, move assign or
+ * move construct it to `Immutable<T>`
+ *
+ * The reason that `Mutable<T>` exists, rather than simply using a `std::unique_ptr<T>`, is to take advantage
+ * of the underlying single-allocation optimization provided by `std::make_shared`.
+ */
+template <class T>
+class Mutable {
+public:
+ Mutable(Mutable&&) = default;
+ Mutable& operator=(Mutable&&) = default;
+
+ Mutable(const Mutable&) = delete;
+ Mutable& operator=(const Mutable&) = delete;
+
+ T* get() { return ptr.get(); }
+ T* operator->() { return ptr.get(); }
+ T& operator*() { return *ptr; }
+
+private:
+ Mutable(std::shared_ptr<T>&& s)
+ : ptr(std::move(s)) {}
+
+ std::shared_ptr<T> ptr;
+
+ template <class S> friend class Immutable;
+ template <class S, class... Args> friend Mutable<S> makeMutable(Args&&...);
+};
+
+template <class T, class... Args>
+Mutable<T> makeMutable(Args&&... args) {
+ return Mutable<T>(std::make_shared<T>(std::forward<Args>(args)...));
+}
+
+/**
+ * `Immutable<T>` is a non-nullable shared reference to a `const T`. Construction requires
+ * a transfer of unique ownership from a `Mutable<T>`; once constructed it has the same behavior
+ * as `std::shared_ptr<const T>` but with better indication of intent.
+ *
+ * Normally one should not share state between threads because it's difficult to verify the
+ * absence of read/write data races. `Immutable` provides a guarantee that no writes are
+ * possible, and instances therefore can be freely transferred and shared between threads.
+ */
+template <class T>
+class Immutable {
+public:
+ template <class S>
+ Immutable(Mutable<S>&& s)
+ : ptr(std::const_pointer_cast<const S>(std::move(s.ptr))) {}
+
+ template <class S>
+ Immutable(Immutable<S>&& s)
+ : ptr(std::move(s.ptr)) {}
+
+ template <class S>
+ Immutable(const Immutable<S>& s)
+ : ptr(s.ptr) {}
+
+ template <class S>
+ Immutable& operator=(Mutable<S>&& s) {
+ ptr = std::const_pointer_cast<const S>(std::move(s.ptr));
+ return *this;
+ }
+
+ template <class S>
+ Immutable& operator=(Immutable<S>&& s) {
+ ptr = std::move(s.ptr);
+ return *this;
+ }
+
+ template <class S>
+ Immutable& operator=(const Immutable<S>& s) {
+ ptr = s.ptr;
+ return *this;
+ }
+
+ const T* get() const { return ptr.get(); }
+ const T* operator->() const { return ptr.get(); }
+ const T& operator*() const { return *ptr; }
+
+ friend bool operator==(const Immutable<T>& lhs, const Immutable<T>& rhs) {
+ return lhs.ptr == rhs.ptr;
+ }
+
+ friend bool operator!=(const Immutable<T>& lhs, const Immutable<T>& rhs) {
+ return lhs.ptr != rhs.ptr;
+ }
+
+private:
+ Immutable(std::shared_ptr<const T>&& s)
+ : ptr(std::move(s)) {}
+
+ std::shared_ptr<const T> ptr;
+
+ template <class S> friend class Immutable;
+ template <class S, class U> friend Immutable<S> staticImmutableCast(const Immutable<U>&);
+};
+
+template <class S, class U>
+Immutable<S> staticImmutableCast(const Immutable<U>& u) {
+ return Immutable<S>(std::static_pointer_cast<const S>(u.ptr));
+}
+
+/**
+ * Constrained mutation of an immutable reference. Makes a temporarily-mutable copy of the
+ * input Immutable using the inner type's copy constructor, runs the given callable on the
+ * mutable copy, and then freezes the copy and reassigns it to the input reference.
+ *
+ * Note that other Immutables referring to the same inner instance are not affected; they
+ * continue to referencing the original immutable instance.
+ */
+template <class T, class Fn>
+void mutate(Immutable<T>& immutable, Fn&& fn) {
+ Mutable<T> mut = makeMutable<T>(*immutable);
+ std::forward<Fn>(fn)(*mut);
+ immutable = std::move(mut);
+}
+
+} // namespace mbgl
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..6738987598 100644
--- a/include/mbgl/util/interpolate.hpp
+++ b/include/mbgl/util/interpolate.hpp
@@ -95,7 +95,11 @@ 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/noncopyable.hpp b/include/mbgl/util/noncopyable.hpp
index 105a76a9a0..8cb7e198d9 100644
--- a/include/mbgl/util/noncopyable.hpp
+++ b/include/mbgl/util/noncopyable.hpp
@@ -7,15 +7,17 @@ namespace non_copyable_
class noncopyable
{
+public:
+ noncopyable( noncopyable const& ) = delete;
+ noncopyable& operator=(noncopyable const& ) = delete;
+
protected:
constexpr noncopyable() = default;
~noncopyable() = default;
- noncopyable( noncopyable const& ) = delete;
- noncopyable& operator=(noncopyable const& ) = delete;
};
} // namespace non_copyable_
-typedef non_copyable_::noncopyable noncopyable;
+using noncopyable = non_copyable_::noncopyable;
} // namespace util
} // namespace mbgl
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index 3cc1146513..f64502c5bc 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 = std::round(std::min(pt.x, t2z));
+ auto y = std::round(std::min(t2z - pt.y, t2z));
+ return { x, y };
+ }
+private:
+ static Point<double> project_(const LatLng& latLng, double worldSize) {
+ return Point<double> {
+ 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/range.hpp b/include/mbgl/util/range.hpp
index f7fa92eb8b..5591a22a1f 100644
--- a/include/mbgl/util/range.hpp
+++ b/include/mbgl/util/range.hpp
@@ -1,3 +1,5 @@
+#include <utility>
+
#pragma once
namespace mbgl {
@@ -5,8 +7,8 @@ namespace mbgl {
template <class T>
class Range {
public:
- constexpr Range(const T& min_, const T& max_)
- : min(min_), max(max_) {}
+ constexpr Range(T min_, T max_)
+ : min(std::move(min_)), max(std::move(max_)) {}
T min;
T max;
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp
index 5236850d83..acbea80273 100644
--- a/include/mbgl/util/run_loop.hpp
+++ b/include/mbgl/util/run_loop.hpp
@@ -16,7 +16,7 @@
namespace mbgl {
namespace util {
-typedef void * LOOP_HANDLE;
+using LOOP_HANDLE = void *;
class RunLoop : public Scheduler,
private util::noncopyable {
@@ -62,14 +62,11 @@ public:
push(task);
return std::make_unique<WorkRequest>(task);
}
-
- // Invoke fn(args...) on this RunLoop, then invoke callback(results...) on the current RunLoop.
- template <class Fn, class... Args>
- std::unique_ptr<AsyncRequest>
- invokeWithCallback(Fn&& fn, Args&&... args) {
- std::shared_ptr<WorkTask> task = WorkTask::makeWithCallback(std::forward<Fn>(fn), std::forward<Args>(args)...);
- push(task);
- return std::make_unique<WorkRequest>(task);
+
+ void schedule(std::weak_ptr<Mailbox> mailbox) override {
+ invoke([mailbox] () {
+ Mailbox::maybeReceive(mailbox);
+ });
}
class Impl;
@@ -81,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..82d317c620 100644
--- a/include/mbgl/util/string.hpp
+++ b/include/mbgl/util/string.hpp
@@ -1,9 +1,34 @@
#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());
+}
+
+} // namespace std
+
+#endif
+
namespace mbgl {
namespace util {
diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp
new file mode 100644
index 0000000000..672eebf6db
--- /dev/null
+++ b/include/mbgl/util/thread.hpp
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/util/platform.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/util.hpp>
+
+#include <cassert>
+#include <future>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <thread>
+#include <utility>
+
+namespace mbgl {
+namespace util {
+
+// Manages a thread with `Object`.
+
+// Upon creation of this object, it launches a thread and creates an object of type `Object`
+// in that thread. When the `Thread<>` object is destructed, the destructor waits
+// for thread termination. The `Thread<>` constructor blocks until the thread and
+// the `Object` are fully created, so after the object creation, it's safe to obtain the
+// `Object` stored in this thread. The thread created will always have low priority on
+// the platforms that support setting thread priority.
+//
+// The following properties make this class different from `ThreadPool`:
+//
+// - Only one thread is created.
+// - `Object` will live in a single thread, providing thread affinity.
+// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>`
+// - A `RunLoop` is created for the `Object` thread.
+// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events.
+//
+template<class Object>
+class Thread : public Scheduler {
+public:
+ template <class... Args>
+ Thread(const std::string& name, Args&&... args) {
+ std::promise<void> running;
+
+ thread = std::thread([&] {
+ platform::setCurrentThreadName(name);
+ platform::makeThreadLowPriority();
+
+ util::RunLoop loop_(util::RunLoop::Type::New);
+ loop = &loop_;
+
+ object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...);
+ running.set_value();
+
+ loop->run();
+ loop = nullptr;
+ });
+
+ running.get_future().get();
+ }
+
+ ~Thread() override {
+ if (paused) {
+ resume();
+ }
+
+ std::promise<void> joinable;
+
+ // Kill the actor, so we don't get more
+ // messages posted on this scheduler after
+ // we delete the RunLoop.
+ loop->invoke([&] {
+ object.reset();
+ joinable.set_value();
+ });
+
+ joinable.get_future().get();
+
+ loop->stop();
+ thread.join();
+ }
+
+ // Returns a non-owning reference to `Object` that
+ // can be used to send messages to `Object`. It is safe
+ // to the non-owning reference to outlive this object
+ // and be used after the `Thread<>` gets destroyed.
+ ActorRef<std::decay_t<Object>> actor() const {
+ return object->self();
+ }
+
+ // Pauses the `Object` thread. It will prevent the object to wake
+ // up from events such as timers and file descriptor I/O. Messages
+ // sent to a paused `Object` will be queued and only processed after
+ // `resume()` is called.
+ void pause() {
+ MBGL_VERIFY_THREAD(tid);
+
+ assert(!paused);
+
+ paused = std::make_unique<std::promise<void>>();
+ resumed = std::make_unique<std::promise<void>>();
+
+ auto pausing = paused->get_future();
+
+ loop->invoke([this] {
+ auto resuming = resumed->get_future();
+ paused->set_value();
+ resuming.get();
+ });
+
+ pausing.get();
+ }
+
+ // Resumes the `Object` thread previously paused by `pause()`.
+ void resume() {
+ MBGL_VERIFY_THREAD(tid);
+
+ assert(paused);
+
+ resumed->set_value();
+
+ resumed.reset();
+ paused.reset();
+ }
+
+private:
+ MBGL_STORE_THREAD(tid);
+
+ void schedule(std::weak_ptr<Mailbox> mailbox) override {
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ queue.push(mailbox);
+ }
+
+ loop->invoke([this] { receive(); });
+ }
+
+ void receive() {
+ std::unique_lock<std::mutex> lock(mutex);
+
+ auto mailbox = queue.front();
+ queue.pop();
+ lock.unlock();
+
+ Mailbox::maybeReceive(mailbox);
+ }
+
+ std::mutex mutex;
+ std::queue<std::weak_ptr<Mailbox>> queue;
+ std::thread thread;
+ std::unique_ptr<Actor<Object>> object;
+
+ std::unique_ptr<std::promise<void>> paused;
+ std::unique_ptr<std::promise<void>> resumed;
+
+ util::RunLoop* loop = nullptr;
+};
+
+} // namespace util
+} // namespace mbgl
diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp
index 1f28a5039a..61aa47d4ea 100644
--- a/include/mbgl/util/tileset.hpp
+++ b/include/mbgl/util/tileset.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/util/range.hpp>
+#include <mbgl/util/constants.hpp>
#include <vector>
#include <string>
@@ -13,11 +14,25 @@ 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.
+
+ friend bool operator==(const Tileset& lhs, const Tileset& rhs) {
+ return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme)
+ == std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme);
+ }
};
} // namespace mbgl
diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp
index 3a4994917b..6e644e2d1f 100644
--- a/include/mbgl/util/unitbezier.hpp
+++ b/include/mbgl/util/unitbezier.hpp
@@ -34,11 +34,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 {
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/include/mbgl/util/work_task.hpp b/include/mbgl/util/work_task.hpp
index dda8e5d00f..f2dcfcfe86 100644
--- a/include/mbgl/util/work_task.hpp
+++ b/include/mbgl/util/work_task.hpp
@@ -18,9 +18,6 @@ public:
template <class Fn, class... Args>
static std::shared_ptr<WorkTask> make(Fn&&, Args&&...);
-
- template <class Fn, class... Args>
- static std::shared_ptr<WorkTask> makeWithCallback(Fn&&, Args&&...);
};
} // namespace mbgl
diff --git a/include/mbgl/util/work_task_impl.hpp b/include/mbgl/util/work_task_impl.hpp
index 8ebc7d45f8..276e0d6237 100644
--- a/include/mbgl/util/work_task_impl.hpp
+++ b/include/mbgl/util/work_task_impl.hpp
@@ -62,48 +62,4 @@ std::shared_ptr<WorkTask> WorkTask::make(Fn&& fn, Args&&... args) {
flag);
}
-namespace detail {
-template <class Tuple, size_t... Indexes>
-auto packageArgumentsAndCallback(std::shared_ptr<std::atomic<bool>> flag,
- Tuple&& args,
- std::index_sequence<Indexes...>) {
- auto callback = std::get<sizeof...(Indexes)>(args);
-
- // Create a lambda L1 that invokes another lambda L2 on the current RunLoop R, that calls
- // the callback C. Both lambdas check the flag before proceeding. L1 needs to check the flag
- // because if the request was cancelled, then R might have been destroyed. L2 needs to check
- // the flag because the request may have been cancelled after L2 was invoked but before it
- // began executing.
-
- auto l2 = [flag, callback] (auto&&... results) {
- if (!*flag) {
- callback(std::forward<decltype(results)>(results)...);
- }
- };
-
- auto l1 = [flag, current = util::RunLoop::Get(), l2_ = l2] (auto&&... results) {
- if (!*flag) {
- current->invoke(l2_, std::forward<decltype(results)>(results)...);
- }
- };
-
- return std::make_tuple(std::get<Indexes>(std::forward<Tuple>(args))..., l1);
-}
-} // namespace detail
-
-template <class Fn, class... Args>
-std::shared_ptr<WorkTask> WorkTask::makeWithCallback(Fn&& fn, Args&&... args) {
- auto flag = std::make_shared<std::atomic<bool>>();
- *flag = false;
-
- auto tuple = detail::packageArgumentsAndCallback(flag,
- std::forward_as_tuple(std::forward<Args>(args)...),
- std::make_index_sequence<sizeof...(Args) - 1>());
-
- return std::make_shared<WorkTaskImpl<std::decay_t<Fn>, decltype(tuple)>>(
- std::forward<Fn>(fn),
- std::move(tuple),
- flag);
-}
-
} // namespace mbgl
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject 8b085a211579d417ad8b3d58bc502c4ffbdfc2e
+Subproject 3634d945477cb7dbbf1e0bebf4cf51542821895
diff --git a/package.json b/package.json
index 68d6b93859..be1e8663f4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@mapbox/mapbox-gl-native",
- "version": "3.5.1",
+ "version": "3.5.5",
"description": "Renders map tiles with Mapbox GL",
"keywords": [
"mapbox",
@@ -13,15 +13,18 @@
},
"license": "BSD-2-Clause",
"dependencies": {
- "node-pre-gyp": "^0.6.28",
- "nan": "^2.4.0"
+ "nan": "^2.4.0",
+ "node-pre-gyp": "^0.6.36",
+ "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",
"lodash": "^4.16.4",
+ "mapbox-gl-styles": "2.0.2",
"pixelmatch": "^4.0.2",
"pngjs": "^3.0.0",
"request": "^2.72.0",
@@ -35,7 +38,9 @@
"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-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 75a6c4d2b9..8ce8b3d3c1 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,9 +2,11 @@
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
-## 5.1.4
+## 5.2.0 - TBA
-* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
+* 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))
+* TBA
### 5.1.4 - September 25, 2017
@@ -21,6 +23,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
* 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
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 018294d462..c96587fce6 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -9,6 +9,8 @@ dependencies {
compile(rootProject.ext.dep.lost) {
exclude group: 'com.google.guava'
}
+ testCompile rootProject.ext.dep.junit
+ testCompile rootProject.ext.dep.mockito
// Mapbox Android Services (GeoJSON support)
compile(rootProject.ext.dep.mapboxJavaGeoJSON) {
@@ -39,11 +41,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 +119,14 @@ android {
}
lintOptions {
+ disable 'MissingTranslation', 'TypographyQuotes'
+ baseline file("lint-baseline-local.xml")
checkAllWarnings true
- warningsAsErrors true
+ warningsAsErrors false
+ }
+
+ testOptions {
+ unitTests.returnDefaultValues = true
}
buildTypes {
@@ -145,3 +156,5 @@ configurations {
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-tests-staticblockremover.gradle b/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle
new file mode 100644
index 0000000000..523dc99dd1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle
@@ -0,0 +1,59 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ mavenLocal()
+ }
+
+ dependencies {
+ classpath 'com.darylteo.gradle:javassist-plugin:0.4.1'
+ }
+}
+
+import com.darylteo.gradle.javassist.tasks.TransformationTask
+import com.darylteo.gradle.javassist.transformers.ClassTransformer
+import javassist.CtClass
+import javassist.CtConstructor
+
+class StaticBlockRemover extends ClassTransformer {
+
+ private static final NATIVE_MAP_VIEW = "com.mapbox.mapboxsdk.maps.NativeMapView";
+ private static
+ final NATIVE_CONNECTIVITY_LISTENER = "com.mapbox.mapboxsdk.net.NativeConnectivityListener";
+ private static final OFFLINE_MANAGER = "com.mapbox.mapboxsdk.offline.OfflineManager";
+ private static final OFFLINE_REGION = "com.mapbox.mapboxsdk.offline.OfflineRegion";
+
+ public void applyTransformations(CtClass clazz) throws Exception {
+ if (shouldFilter(clazz)) {
+ CtConstructor constructor = clazz.getClassInitializer()
+ if (constructor != null) {
+ clazz.removeConstructor(constructor)
+ }
+ }
+ }
+
+ public boolean shouldFilter(CtClass clazz) {
+ return hasAStaticBlock(clazz);
+ }
+
+ private boolean hasAStaticBlock(CtClass clazz) {
+ String name = clazz.getName();
+ boolean isNativeMapView = name.equals(NATIVE_MAP_VIEW);
+ boolean isNativeConnectivityListener = name.equals(NATIVE_CONNECTIVITY_LISTENER);
+ boolean isOfflineManager = name.equals(OFFLINE_MANAGER);
+ boolean isOfflineRegion = name.equals(OFFLINE_REGION);
+
+ return isNativeMapView || isNativeConnectivityListener || isOfflineManager || isOfflineRegion;
+ }
+}
+
+task removeStatic(type: TransformationTask) {
+ // TODO Find a better way to get output classes path
+ String fromToDirPath = buildDir.getAbsolutePath() + "/intermediates/classes/debug"
+ from fromToDirPath
+ transformation = new StaticBlockRemover()
+ into fromToDirPath
+}
+
+afterEvaluate {
+ compileDebugUnitTestSources.dependsOn(removeStatic)
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index 078fdbc801..a9ec9fae88 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.mapbox.mapboxsdk
-VERSION_NAME=5.2.0-SNAPSHOT
+VERSION_NAME=5.2.0-beta-SNAPSHOT
POM_DESCRIPTION=Mapbox GL Android SDK
POM_URL=https://github.com/mapbox/mapbox-gl-native
@@ -17,4 +17,4 @@ 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
+android.buildOnlyTargetAbi=true \ No newline at end of file
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/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
new file mode 100644
index 0000000000..8a75176ccd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
@@ -0,0 +1,15 @@
+package com.mapbox.mapboxsdk;
+
+/**
+ * Centralises the knowledge about "mapbox-gl" library loading.
+ */
+public class LibraryLoader {
+
+ /**
+ * Loads "libmapbox-gl.so" native shared library.
+ */
+ public static void load() {
+ System.loadLibrary("mapbox-gl");
+ }
+
+}
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 eadc3fdcf5..7fd9d6172d 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,5 +1,6 @@
package com.mapbox.mapboxsdk;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -25,8 +26,10 @@ import timber.log.Timber;
* connectivity state.
* </p>
*/
+@UiThread
public final class Mapbox {
+ @SuppressLint("StaticFieldLeak")
private static Mapbox INSTANCE;
private Context context;
private String accessToken;
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/IconFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
index f9ca9bf4cc..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;
@@ -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;
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 8704e882ea..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
@@ -30,7 +30,10 @@ 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;
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/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
index 1ee59057d2..97a9ea94ee 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.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java
new file mode 100644
index 0000000000..7fc70716da
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java
@@ -0,0 +1,293 @@
+package com.mapbox.mapboxsdk.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/egl/EGLConfigException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java
new file mode 100644
index 0000000000..3f576d0eda
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java
@@ -0,0 +1,21 @@
+package com.mapbox.mapboxsdk.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/geometry/LatLngQuad.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java
new file mode 100644
index 0000000000..e374eee8f3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java
@@ -0,0 +1,87 @@
+package com.mapbox.mapboxsdk.geometry;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A geographical area representing a non-aligned quadrilateral
+ * <p>
+ * This class does not wrap values to the world bounds
+ * </p>
+ */
+public class LatLngQuad implements Parcelable {
+
+ private final LatLng topLeft;
+ private final LatLng topRight;
+ private final LatLng bottomRight;
+ private final LatLng bottomLeft;
+
+ /**
+ * Construct a new LatLngQuad based on its corners,
+ * in order top left, top right, bottom left, bottom right
+ */
+ public LatLngQuad(final LatLng topLeft, final LatLng topRight, final LatLng bottomRight, final LatLng bottomLeft) {
+ this.topLeft = topLeft;
+ this.topRight = topRight;
+ this.bottomRight = bottomRight;
+ this.bottomLeft = bottomLeft;
+ }
+
+ public LatLng getTopLeft() {
+ return this.topLeft;
+ }
+
+ public LatLng getTopRight() {
+ return this.topRight;
+ }
+
+ public LatLng getBottomRight() {
+ return this.bottomRight;
+ }
+
+ public LatLng getBottomLeft() {
+ return this.bottomLeft;
+ }
+
+ public static final Parcelable.Creator<LatLngQuad> CREATOR = new Parcelable.Creator<LatLngQuad>() {
+ @Override
+ public LatLngQuad createFromParcel(final Parcel in) {
+ return readFromParcel(in);
+ }
+
+ @Override
+ public LatLngQuad[] newArray(final int size) {
+ return new LatLngQuad[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ int code = topLeft.hashCode();
+ code = (code ^ code >>> 31) + topRight.hashCode();
+ code = (code ^ code >>> 31) + bottomRight.hashCode();
+ code = (code ^ code >>> 31) + bottomLeft.hashCode();
+ return code;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel out, final int arg1) {
+ topLeft.writeToParcel(out, arg1);
+ topRight.writeToParcel(out, arg1);
+ bottomRight.writeToParcel(out, arg1);
+ bottomLeft.writeToParcel(out, arg1);
+ }
+
+ private static LatLngQuad readFromParcel(final Parcel in) {
+ final LatLng topLeft = new LatLng(in);
+ final LatLng topRight = new LatLng(in);
+ final LatLng bottomRight = new LatLng(in);
+ final LatLng bottomLeft = new LatLng(in);
+ return new LatLngQuad(topLeft, topRight, bottomRight, bottomLeft);
+ }
+}
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 7f3a48c57a..e2626a026b 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
@@ -110,13 +110,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;
@@ -161,15 +159,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/maps/AnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java
new file mode 100644
index 0000000000..939fadc9c2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java
@@ -0,0 +1,86 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates {@link Annotation}'s functionality..
+ */
+class AnnotationContainer implements Annotations {
+
+ private final NativeMapView nativeMapView;
+ private final LongSparseArray<Annotation> annotations;
+
+ AnnotationContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+ this.nativeMapView = nativeMapView;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public Annotation obtainBy(long id) {
+ return annotations.get(id);
+ }
+
+ @Override
+ public List<Annotation> obtainAll() {
+ List<Annotation> annotations = new ArrayList<>();
+ for (int i = 0; i < this.annotations.size(); i++) {
+ annotations.add(this.annotations.get(this.annotations.keyAt(i)));
+ }
+ return annotations;
+ }
+
+ @Override
+ public void removeBy(long id) {
+ if (nativeMapView != null) {
+ nativeMapView.removeAnnotation(id);
+ }
+ annotations.remove(id);
+ }
+
+ @Override
+ public void removeBy(@NonNull Annotation annotation) {
+ long id = annotation.getId();
+ removeBy(id);
+ }
+
+ @Override
+ public void removeBy(@NonNull List<? extends Annotation> annotationList) {
+ int count = annotationList.size();
+ long[] ids = new long[count];
+ for (int i = 0; i < count; i++) {
+ ids[i] = annotationList.get(i).getId();
+ }
+
+ removeNativeAnnotations(ids);
+
+ for (long id : ids) {
+ annotations.remove(id);
+ }
+ }
+
+ @Override
+ public void removeAll() {
+ int count = annotations.size();
+ long[] ids = new long[count];
+ for (int i = 0; i < count; i++) {
+ ids[i] = annotations.keyAt(i);
+ }
+
+ removeNativeAnnotations(ids);
+
+ annotations.clear();
+ }
+
+ private void removeNativeAnnotations(long[] ids) {
+ if (nativeMapView != null) {
+ nativeMapView.removeAnnotations(ids);
+ }
+ }
+} \ 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 d15d5eddf8..c09c926eb5 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
@@ -14,7 +14,6 @@ import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
-import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerView;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
@@ -45,12 +44,11 @@ class AnnotationManager {
private static final String LAYER_ID_SHAPE_ANNOTATIONS = "com.mapbox.annotations.shape.";
private static final long NO_ANNOTATION_ID = -1;
- private final NativeMapView nativeMapView;
private final MapView mapView;
private final IconManager iconManager;
private final InfoWindowManager infoWindowManager = new InfoWindowManager();
private final MarkerViewManager markerViewManager;
- private final LongSparseArray<Annotation> annotations = new LongSparseArray<>();
+ private final LongSparseArray<Annotation> annotationsArray;
private final List<Marker> selectedMarkers = new ArrayList<>();
private final List<String> shapeAnnotationIds = new ArrayList<>();
@@ -59,11 +57,22 @@ class AnnotationManager {
private MapboxMap.OnPolygonClickListener onPolygonClickListener;
private MapboxMap.OnPolylineClickListener onPolylineClickListener;
- AnnotationManager(NativeMapView view, MapView mapView, MarkerViewManager markerViewManager) {
- this.nativeMapView = view;
+ private Annotations annotations;
+ 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.mapView = mapView;
- this.iconManager = new IconManager(nativeMapView);
+ this.annotationsArray = annotationsArray;
this.markerViewManager = markerViewManager;
+ this.iconManager = iconManager;
+ this.annotations = annotations;
+ this.markers = markers;
+ this.polygons = polygons;
+ this.polylines = polylines;
if (view != null) {
// null checking needed for unit tests
view.addOnMapChangedListener(markerViewManager);
@@ -88,15 +97,15 @@ class AnnotationManager {
//
Annotation getAnnotation(long id) {
- return annotations.get(id);
+ return annotations.obtainBy(id);
}
List<Annotation> getAnnotations() {
- List<Annotation> annotations = new ArrayList<>();
- for (int i = 0; i < this.annotations.size(); i++) {
- annotations.add(this.annotations.get(this.annotations.keyAt(i)));
- }
- return annotations;
+ return annotations.obtainAll();
+ }
+
+ void removeAnnotation(long id) {
+ annotations.removeBy(id);
}
void removeAnnotation(@NonNull Annotation annotation) {
@@ -109,30 +118,19 @@ class AnnotationManager {
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ // do icon cleanup
+ iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
shapeAnnotationIds.remove(annotation.getId());
}
- long id = annotation.getId();
- if (nativeMapView != null) {
- nativeMapView.removeAnnotation(id);
- }
- annotations.remove(id);
- }
-
- void removeAnnotation(long id) {
- if (nativeMapView != null) {
- nativeMapView.removeAnnotation(id);
- }
- annotations.remove(id);
+ annotations.removeBy(annotation);
}
void removeAnnotations(@NonNull List<? extends Annotation> annotationList) {
- int count = annotationList.size();
- long[] ids = new long[count];
- for (int i = 0; i < count; i++) {
- Annotation annotation = annotationList.get(i);
+ for (Annotation annotation : annotationList) {
if (annotation instanceof Marker) {
Marker marker = (Marker) annotation;
marker.hideInfoWindow();
@@ -142,48 +140,39 @@ class AnnotationManager {
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
shapeAnnotationIds.remove(annotation.getId());
}
- ids[i] = annotationList.get(i).getId();
- }
-
- if (nativeMapView != null) {
- nativeMapView.removeAnnotations(ids);
- }
-
- for (long id : ids) {
- annotations.remove(id);
}
+ annotations.removeBy(annotationList);
}
void removeAnnotations() {
Annotation annotation;
- int count = annotations.size();
+ int count = annotationsArray.size();
long[] ids = new long[count];
selectedMarkers.clear();
for (int i = 0; i < count; i++) {
- ids[i] = annotations.keyAt(i);
- annotation = annotations.get(ids[i]);
+ ids[i] = annotationsArray.keyAt(i);
+ annotation = annotationsArray.get(ids[i]);
if (annotation instanceof Marker) {
Marker marker = (Marker) annotation;
marker.hideInfoWindow();
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
shapeAnnotationIds.remove(annotation.getId());
}
}
-
- if (nativeMapView != null) {
- nativeMapView.removeAnnotations(ids);
- }
-
- annotations.clear();
+ annotations.removeAll();
}
//
@@ -191,134 +180,109 @@ class AnnotationManager {
//
Marker addMarker(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) {
- Marker marker = prepareMarker(markerOptions);
- long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0;
- marker.setMapboxMap(mapboxMap);
- marker.setId(id);
- annotations.put(id, marker);
- return marker;
+ return markers.addBy(markerOptions, mapboxMap);
}
List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap) {
- int count = markerOptionsList.size();
- List<Marker> markers = new ArrayList<>(count);
- if (count > 0) {
- BaseMarkerOptions markerOptions;
- Marker marker;
- for (int i = 0; i < count; i++) {
- markerOptions = markerOptionsList.get(i);
- marker = prepareMarker(markerOptions);
- markers.add(marker);
- }
-
- if (markers.size() > 0) {
- long[] ids;
- if (nativeMapView != null) {
- ids = nativeMapView.addMarkers(markers);
- } else {
- ids = new long[markers.size()];
- }
-
- long id;
- Marker m;
- for (int i = 0; i < ids.length; i++) {
- m = markers.get(i);
- m.setMapboxMap(mapboxMap);
- id = ids[i];
- m.setId(id);
- annotations.put(id, m);
- }
+ return markers.addBy(markerOptionsList, mapboxMap);
+ }
- }
+ void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) {
+ if (!isAddedToMap(updatedMarker)) {
+ logNonAdded(updatedMarker);
+ return;
}
- return markers;
+ markers.update(updatedMarker, mapboxMap);
+ }
+
+ List<Marker> getMarkers() {
+ return markers.obtainAll();
}
- private Marker prepareMarker(BaseMarkerOptions markerOptions) {
- Marker marker = markerOptions.getMarker();
- Icon icon = iconManager.loadIconForMarker(marker);
- marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon));
- return marker;
+ @NonNull
+ List<Marker> getMarkersInRect(@NonNull RectF rectangle) {
+ return markers.obtainAllIn(rectangle);
}
MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap,
@Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) {
- final MarkerView marker = prepareViewMarker(markerOptions);
-
- // add marker to map
- marker.setMapboxMap(mapboxMap);
- long id = nativeMapView.addMarker(marker);
- marker.setId(id);
- annotations.put(id, marker);
-
- if (onMarkerViewAddedListener != null) {
- markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener);
- }
- markerViewManager.setEnabled(true);
- markerViewManager.setWaitingForRenderInvoke(true);
- return marker;
+ return markers.addViewBy(markerOptions, mapboxMap, onMarkerViewAddedListener);
}
List<MarkerView> addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions,
@NonNull MapboxMap mapboxMap) {
- List<MarkerView> markers = new ArrayList<>();
- for (BaseMarkerViewOptions markerViewOption : markerViewOptions) {
- // if last marker
- if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) {
- // get notified when render occurs to invalidate and draw MarkerViews
- markerViewManager.setWaitingForRenderInvoke(true);
- }
- // add marker to map
- MarkerView marker = prepareViewMarker(markerViewOption);
- marker.setMapboxMap(mapboxMap);
- long id = nativeMapView.addMarker(marker);
- marker.setId(id);
- annotations.put(id, marker);
- markers.add(marker);
- }
- markerViewManager.setEnabled(true);
- markerViewManager.update();
- return markers;
+ return markers.addViewsBy(markerViewOptions, mapboxMap);
}
- private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) {
- MarkerView marker = markerViewOptions.getMarker();
- iconManager.loadIconForMarkerView(marker);
- return marker;
+ List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) {
+ return markers.obtainViewsIn(rectangle);
}
- void updateMarker(@NonNull Marker updatedMarker) {
- if (!isAddedToMap(updatedMarker)) {
- Timber.w("Attempting to update non-added Marker with value %s", updatedMarker);
+ void reloadMarkers() {
+ markers.reload();
+ }
+
+ //
+ // Polygons
+ //
+
+ Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
+ Polygon polygon = polygons.addBy(polygonOptions, mapboxMap);
+ shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId());
+ return polygon;
+ }
+
+ List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
+ List<Polygon> polygonList = polygons.addBy(polygonOptionsList, mapboxMap);
+ for (Polygon polygon : polygonList) {
+ shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId());
+ }
+ return polygonList;
+ }
+
+ void updatePolygon(Polygon polygon) {
+ if (!isAddedToMap(polygon)) {
+ logNonAdded(polygon);
return;
}
- ensureIconLoaded(updatedMarker);
- nativeMapView.updateMarker(updatedMarker);
- annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker);
+ polygons.update(polygon);
}
- private boolean isAddedToMap(Annotation annotation) {
- return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) > -1;
+ List<Polygon> getPolygons() {
+ return polygons.obtainAll();
+ }
+
+ //
+ // Polylines
+ //
+
+ Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
+ Polyline polyline = polylines.addBy(polylineOptions, mapboxMap);
+ shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId());
+ return polyline;
}
- private void ensureIconLoaded(Marker marker) {
- if (!(marker instanceof MarkerView)) {
- iconManager.ensureIconLoaded(marker, mapboxMap);
+ List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
+ List<Polyline> polylineList = polylines.addBy(polylineOptionsList, mapboxMap);
+ for (Polyline polyline : polylineList) {
+ shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId());
}
+ return polylineList;
}
- List<Marker> getMarkers() {
- List<Marker> markers = new ArrayList<>();
- Annotation annotation;
- for (int i = 0; i < annotations.size(); i++) {
- annotation = annotations.get(annotations.keyAt(i));
- if (annotation instanceof Marker) {
- markers.add((Marker) annotation);
- }
+ void updatePolyline(Polyline polyline) {
+ if (!isAddedToMap(polyline)) {
+ logNonAdded(polyline);
+ return;
}
- return markers;
+ polylines.update(polyline);
}
+ List<Polyline> getPolylines() {
+ return polylines.obtainAll();
+ }
+
+ // TODO Refactor from here still in progress
void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) {
onMarkerClickListener = listener;
}
@@ -393,205 +357,6 @@ class AnnotationManager {
return selectedMarkers;
}
- @NonNull
- List<Marker> getMarkersInRect(@NonNull RectF rectangle) {
- // convert Rectangle to be density dependent
- float pixelRatio = nativeMapView.getPixelRatio();
- RectF rect = new RectF(rectangle.left / pixelRatio,
- rectangle.top / pixelRatio,
- rectangle.right / pixelRatio,
- rectangle.bottom / pixelRatio);
-
- long[] ids = nativeMapView.queryPointAnnotations(rect);
-
- List<Long> idsList = new ArrayList<>(ids.length);
- for (long id : ids) {
- idsList.add(id);
- }
-
- List<Marker> annotations = new ArrayList<>(ids.length);
- List<Annotation> annotationList = getAnnotations();
- int count = annotationList.size();
- for (int i = 0; i < count; i++) {
- Annotation annotation = annotationList.get(i);
- if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) {
- annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation);
- }
- }
-
- return new ArrayList<>(annotations);
- }
-
- List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) {
- float pixelRatio = nativeMapView.getPixelRatio();
- RectF rect = new RectF(rectangle.left / pixelRatio,
- rectangle.top / pixelRatio,
- rectangle.right / pixelRatio,
- rectangle.bottom / pixelRatio);
-
- long[] ids = nativeMapView.queryPointAnnotations(rect);
-
- List<Long> idsList = new ArrayList<>(ids.length);
- for (long id : ids) {
- idsList.add(id);
- }
-
- List<MarkerView> annotations = new ArrayList<>(ids.length);
- List<Annotation> annotationList = getAnnotations();
- int count = annotationList.size();
- for (int i = 0; i < count; i++) {
- Annotation annotation = annotationList.get(i);
- if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) {
- annotations.add((MarkerView) annotation);
- }
- }
-
- return new ArrayList<>(annotations);
- }
-
- //
- // Polygons
- //
-
- Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
- Polygon polygon = polygonOptions.getPolygon();
- if (!polygon.getPoints().isEmpty()) {
- long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0;
- polygon.setId(id);
- polygon.setMapboxMap(mapboxMap);
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id);
- annotations.put(id, polygon);
- }
- return polygon;
- }
-
- List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
- int count = polygonOptionsList.size();
-
- Polygon polygon;
- List<Polygon> polygons = new ArrayList<>(count);
- if (count > 0) {
- for (PolygonOptions polygonOptions : polygonOptionsList) {
- polygon = polygonOptions.getPolygon();
- if (!polygon.getPoints().isEmpty()) {
- polygons.add(polygon);
- }
- }
-
- long[] ids;
- if (nativeMapView != null) {
- ids = nativeMapView.addPolygons(polygons);
- } else {
- ids = new long[polygons.size()];
- }
-
- long id;
- for (int i = 0; i < ids.length; i++) {
- polygon = polygons.get(i);
- polygon.setMapboxMap(mapboxMap);
- id = ids[i];
- polygon.setId(id);
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id);
- annotations.put(id, polygon);
- }
- }
- return polygons;
- }
-
- void updatePolygon(@NonNull 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);
- }
-
- List<Polygon> getPolygons() {
- List<Polygon> polygons = new ArrayList<>();
- Annotation annotation;
- for (int i = 0; i < annotations.size(); i++) {
- annotation = annotations.get(annotations.keyAt(i));
- if (annotation instanceof Polygon) {
- polygons.add((Polygon) annotation);
- }
- }
- return polygons;
- }
-
- //
- // Polylines
- //
-
- Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
- Polyline polyline = polylineOptions.getPolyline();
- if (!polyline.getPoints().isEmpty()) {
- long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0;
- polyline.setMapboxMap(mapboxMap);
- polyline.setId(id);
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id);
- annotations.put(id, polyline);
- }
- return polyline;
- }
-
- List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
- int count = polylineOptionsList.size();
- Polyline polyline;
- List<Polyline> polylines = new ArrayList<>(count);
-
- if (count > 0) {
- for (PolylineOptions options : polylineOptionsList) {
- polyline = options.getPolyline();
- if (!polyline.getPoints().isEmpty()) {
- polylines.add(polyline);
- }
- }
-
- long[] ids;
- if (nativeMapView != null) {
- ids = nativeMapView.addPolylines(polylines);
- } else {
- ids = new long[polylines.size()];
- }
-
- long id;
- Polyline p;
- for (int i = 0; i < ids.length; i++) {
- p = polylines.get(i);
- p.setMapboxMap(mapboxMap);
- id = ids[i];
- p.setId(id);
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id);
- annotations.put(id, p);
- }
- }
- return polylines;
- }
-
- void updatePolyline(@NonNull Polyline polyline) {
- if (!isAddedToMap(polyline)) {
- Timber.w("Attempting to update non-added Polyline with value %s", polyline);
- return;
- }
-
- nativeMapView.updatePolyline(polyline);
- annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline);
- }
-
- List<Polyline> getPolylines() {
- List<Polyline> polylines = new ArrayList<>();
- Annotation annotation;
- for (int i = 0; i < annotations.size(); i++) {
- annotation = annotations.get(annotations.keyAt(i));
- if (annotation instanceof Polyline) {
- polylines.add((Polyline) annotation);
- }
- }
- return polylines;
- }
-
InfoWindowManager getInfoWindowManager() {
return infoWindowManager;
}
@@ -601,9 +366,9 @@ class AnnotationManager {
}
void adjustTopOffsetPixels(MapboxMap mapboxMap) {
- int count = annotations.size();
+ int count = annotationsArray.size();
for (int i = 0; i < count; i++) {
- Annotation annotation = annotations.get(i);
+ Annotation annotation = annotationsArray.get(i);
if (annotation instanceof Marker) {
Marker marker = (Marker) annotation;
marker.setTopOffsetPixels(
@@ -619,18 +384,12 @@ class AnnotationManager {
}
}
- void reloadMarkers() {
- iconManager.reloadIcons();
- int count = annotations.size();
- for (int i = 0; i < count; i++) {
- Annotation annotation = annotations.get(i);
- if (annotation instanceof Marker) {
- Marker marker = (Marker) annotation;
- nativeMapView.removeAnnotation(annotation.getId());
- long newId = nativeMapView.addMarker(marker);
- marker.setId(newId);
- }
- }
+ 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);
}
//
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java
new file mode 100644
index 0000000000..ae41cbb0cb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java
@@ -0,0 +1,25 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Annotation}'s collection.
+ */
+interface Annotations {
+ Annotation obtainBy(long id);
+
+ List<Annotation> obtainAll();
+
+ void removeBy(long id);
+
+ void removeBy(@NonNull Annotation annotation);
+
+ void removeBy(@NonNull List<? extends Annotation> annotationList);
+
+ void removeAll();
+}
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 18eecfd9c3..b1d6df2103 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 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));
}
@@ -83,13 +82,13 @@ 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);
}
}
@@ -121,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) {
@@ -149,4 +141,23 @@ class IconManager {
marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon));
}
}
+
+ void iconCleanup(Icon icon) {
+ int refCounter = iconMap.get(icon) - 1;
+ 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/InfoWindowManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java
index 11100d6f17..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
@@ -21,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;
@@ -29,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();
+ }
}
}
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 bff5e9bed2..2394e52193 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
@@ -693,4 +693,4 @@ final class MapGestureDetector {
void setOnScrollListener(MapboxMap.OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
-} \ No newline at end of file
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index 9fa3126523..12e4c675ee 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,34 +1,34 @@
package com.mapbox.mapboxsdk.maps;
import android.content.Context;
-import android.graphics.Canvas;
import android.graphics.PointF;
-import android.graphics.SurfaceTexture;
+import android.os.Build;
+import android.opengl.GLSurfaceView;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.CallSuper;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.support.v4.util.LongSparseArray;
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.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.egl.EGLConfigChooser;
+import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
@@ -40,6 +40,16 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION;
+import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE;
+import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY;
/**
* <p>
@@ -57,17 +67,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 GLSurfaceView glSurfaceView;
@UiThread
public MapView(@NonNull Context context) {
@@ -98,24 +117,39 @@ public class MapView extends FrameLayout {
// 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);
+
+ 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();
+ }
+ });
+ }
- // create native Map object
- nativeMapView = new NativeMapView(this);
+ 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();
@@ -124,30 +158,45 @@ 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));
- AnnotationManager annotations = new AnnotationManager(nativeMapView, this, markerViewManager);
- Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings,
+ IconManager iconManager = new IconManager(nativeMapView);
+ Annotations annotations = new AnnotationContainer(nativeMapView, annotationsArray);
+ Markers markers = new MarkerContainer(nativeMapView, this, annotationsArray, iconManager, markerViewManager);
+ Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray);
+ Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray);
+ AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray,
+ markerViewManager, iconManager, annotations, markers, polygons, polylines);
+ Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings,
cameraChangeDispatcher);
+
mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj,
- registerTouchListener, annotations, cameraChangeDispatcher);
+ registerTouchListener, annotationManager, cameraChangeDispatcher);
+ focalPointInvalidator.addListener(mapboxMap.createFocalPointChangeListener());
+
+ mapCallback.attachMapboxMap(mapboxMap);
// user input
- mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations,
- cameraChangeDispatcher);
+ mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings,
+ annotationManager, cameraChangeDispatcher);
mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
+ mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this));
MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform);
- mapZoomButtonController = new MapZoomButtonController(this, uiSettings, zoomListener);
+ 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));
@@ -158,14 +207,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);
+ }
+ }
+ };
}
//
@@ -188,25 +281,40 @@ public class MapView extends FrameLayout {
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) {
- TextureView textureView = new TextureView(getContext());
- textureView.setSurfaceTextureListener(new SurfaceTextureListener());
- addView(textureView, 0);
- } else {
- SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
- surfaceView.getHolder().addCallback(new SurfaceCallback());
- surfaceView.setVisibility(View.VISIBLE);
- }
+ private void initialiseDrawingSurface() {
+ glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView);
+ glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
+ glSurfaceView.setEGLContextClientVersion(2);
+ glSurfaceView.setEGLConfigChooser(new EGLConfigChooser());
+
+ MapRenderer mapRenderer = new MapRenderer(getContext(), glSurfaceView) {
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ MapView.this.post(new Runnable() {
+ @Override
+ public void run() {
+ // Initialise only once
+ if (mapboxMap == null) {
+ initialiseMap();
+ mapboxMap.onStart();
+ }
+ }
+ });
+
+ super.onSurfaceCreated(gl, config);
+ }
+ };
+
+ glSurfaceView.setRenderer(mapRenderer);
+ glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
+ glSurfaceView.setVisibility(View.VISIBLE);
+
+ nativeMapView = new NativeMapView(this, mapRenderer);
+ nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight());
}
/**
@@ -226,8 +334,10 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onStart() {
- mapboxMap.onStart();
ConnectivityReceiver.instance(getContext()).activate();
+ if (mapboxMap != null) {
+ mapboxMap.onStart();
+ }
}
/**
@@ -235,7 +345,9 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onResume() {
- // replaced by onStart in v5.0.0
+ if (glSurfaceView != null) {
+ glSurfaceView.onResume();
+ }
}
/**
@@ -243,7 +355,9 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onPause() {
- // replaced by onStop in v5.0.0
+ if (glSurfaceView != null) {
+ glSurfaceView.onPause();
+ }
}
/**
@@ -261,9 +375,6 @@ public class MapView extends FrameLayout {
@UiThread
public void onDestroy() {
destroyed = true;
- nativeMapView.terminateContext();
- nativeMapView.terminateDisplay();
- nativeMapView.destroySurface();
mapCallback.clearOnMapReadyCallbacks();
nativeMapView.destroy();
nativeMapView = null;
@@ -328,21 +439,6 @@ public class MapView extends FrameLayout {
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.
@@ -375,7 +471,10 @@ public class MapView extends FrameLayout {
if (destroyed) {
return;
}
-
+ if (nativeMapView == null) {
+ mapboxMapOptions.styleUrl(url);
+ return;
+ }
nativeMapView.setStyleUrl(url);
}
@@ -383,119 +482,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() && nativeMapView != null) {
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
//
@@ -505,26 +502,37 @@ public class MapView extends FrameLayout {
@CallSuper
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mapZoomButtonController != null) {
- mapZoomButtonController.setVisible(false);
- }
+ mapZoomButtonController.setVisible(false);
}
// 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 (mapZoomButtonController != null) {
+ 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>P
+ * <p>
* Add a callback that's invoked when the displayed map view changes.
* </p>
* To remove the callback, use {@link MapView#removeOnMapChangedListener(OnMapChangedListener)}.
@@ -534,7 +542,7 @@ public class MapView extends FrameLayout {
*/
public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
- nativeMapView.addOnMapChangedListener(listener);
+ onMapChangedListeners.add(listener);
}
}
@@ -546,7 +554,7 @@ public class MapView extends FrameLayout {
*/
public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
- nativeMapView.removeOnMapChangedListener(listener);
+ onMapChangedListeners.remove(listener);
}
}
@@ -813,10 +821,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
@@ -910,11 +918,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;
}
@@ -922,14 +930,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) {
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 e074d938fd..0c820d844c 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
@@ -59,6 +59,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;
@@ -74,6 +75,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,
@@ -99,6 +101,7 @@ public final class MapboxMap {
setDebugActive(options.getDebugActive());
setApiBaseUrl(options);
setStyleUrl(options);
+ setPrefetchesTiles(options);
}
/**
@@ -209,7 +212,6 @@ public final class MapboxMap {
*
* @return Duration in milliseconds
*/
- @UiThread
public long getTransitionDuration() {
return nativeMapView.getTransitionDuration();
}
@@ -219,7 +221,6 @@ public final class MapboxMap {
*
* @param durationMs Duration in milliseconds
*/
- @UiThread
public void setTransitionDuration(long durationMs) {
nativeMapView.setTransitionDuration(durationMs);
}
@@ -232,7 +233,6 @@ public final class MapboxMap {
*
* @return Delay in milliseconds
*/
- @UiThread
public long getTransitionDelay() {
return nativeMapView.getTransitionDelay();
}
@@ -242,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();
}
@@ -264,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);
}
@@ -277,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;
}
}
@@ -293,7 +318,6 @@ public final class MapboxMap {
*
* @param layer the layer to add
*/
- @UiThread
public void addLayer(@NonNull Layer layer) {
nativeMapView.addLayer(layer);
}
@@ -304,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);
}
@@ -315,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);
}
@@ -327,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);
}
@@ -338,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);
@@ -350,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);
@@ -362,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);
@@ -373,7 +391,6 @@ public final class MapboxMap {
*
* @return all the sources in the current style
*/
- @UiThread
public List<Source> getSources() {
return nativeMapView.getSources();
}
@@ -385,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);
}
@@ -398,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;
}
}
@@ -414,7 +429,6 @@ public final class MapboxMap {
*
* @param source the source to add
*/
- @UiThread
public void addSource(@NonNull Source source) {
nativeMapView.addSource(source);
}
@@ -425,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);
@@ -437,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);
@@ -449,7 +461,6 @@ 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);
}
@@ -459,11 +470,14 @@ public final class MapboxMap {
*
* @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
//
@@ -475,7 +489,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);
@@ -483,12 +496,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();
}
@@ -501,10 +513,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);
@@ -517,7 +531,6 @@ public final class MapboxMap {
*
* @return The maximum zoom level.
*/
- @UiThread
public double getMaxZoomLevel() {
return transform.getMaxZoom();
}
@@ -543,7 +556,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/plugins/locationlayer instead.
*/
+ @Deprecated
public TrackingSettings getTrackingSettings() {
return trackingSettings;
}
@@ -556,7 +572,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/plugins/locationlayer instead.
*/
+ @Deprecated
public MyLocationViewSettings getMyLocationViewSettings() {
return myLocationViewSettings;
}
@@ -594,6 +613,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.
@@ -632,7 +692,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);
}
@@ -645,7 +704,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
@@ -666,7 +724,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);
}
@@ -681,7 +738,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);
}
@@ -705,7 +761,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);
}
@@ -724,7 +779,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);
}
@@ -750,7 +804,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);
@@ -778,7 +831,6 @@ 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) {
new Handler().post(new Runnable() {
@@ -798,7 +850,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);
}
@@ -815,7 +866,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);
}
@@ -831,7 +881,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);
}
@@ -854,7 +903,6 @@ 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) {
new Handler().post(new Runnable() {
@@ -925,7 +973,6 @@ public final class MapboxMap {
*
* @return If true, map debug information is currently shown.
*/
- @UiThread
public boolean isDebugActive() {
return nativeMapView.getDebug();
}
@@ -938,7 +985,6 @@ public final class MapboxMap {
*
* @param debugActive If true, map debug information is shown.
*/
- @UiThread
public void setDebugActive(boolean debugActive) {
nativeMapView.setDebug(debugActive);
}
@@ -952,7 +998,6 @@ public final class MapboxMap {
*
* @see #isDebugActive()
*/
- @UiThread
public void cycleDebugOptions() {
nativeMapView.cycleDebugOptions();
}
@@ -1002,7 +1047,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);
}
@@ -1035,7 +1079,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() {
@@ -1067,7 +1110,6 @@ public final class MapboxMap {
* @param style The bundled style.
* @see Style
*/
- @UiThread
public void setStyle(@Style.StyleUrl String style) {
setStyleUrl(style);
}
@@ -1084,7 +1126,6 @@ public final class MapboxMap {
* @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);
}
@@ -1102,16 +1143,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
//
@@ -1126,7 +1187,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);
@@ -1142,7 +1202,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);
@@ -1157,9 +1216,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);
}
@@ -1174,8 +1235,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) {
@@ -1191,9 +1254,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);
@@ -1204,9 +1269,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);
}
@@ -1221,7 +1288,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) {
@@ -1235,9 +1301,8 @@ public final class MapboxMap {
*
* @param updatedMarker An updated marker object
*/
- @UiThread
public void updateMarker(@NonNull Marker updatedMarker) {
- annotationManager.updateMarker(updatedMarker);
+ annotationManager.updateMarker(updatedMarker, this);
}
/**
@@ -1246,7 +1311,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);
@@ -1258,7 +1322,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);
@@ -1269,7 +1332,6 @@ public final class MapboxMap {
*
* @param polyline An updated polyline object.
*/
- @UiThread
public void updatePolyline(Polyline polyline) {
annotationManager.updatePolyline(polyline);
}
@@ -1280,7 +1342,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);
@@ -1292,7 +1353,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);
@@ -1303,7 +1363,6 @@ public final class MapboxMap {
*
* @param polygon An updated polygon object
*/
- @UiThread
public void updatePolygon(Polygon polygon) {
annotationManager.updatePolygon(polygon);
}
@@ -1316,7 +1375,6 @@ public final class MapboxMap {
*
* @param marker Marker to remove
*/
- @UiThread
public void removeMarker(@NonNull Marker marker) {
annotationManager.removeAnnotation(marker);
}
@@ -1329,7 +1387,6 @@ public final class MapboxMap {
*
* @param polyline Polyline to remove
*/
- @UiThread
public void removePolyline(@NonNull Polyline polyline) {
annotationManager.removeAnnotation(polyline);
}
@@ -1342,7 +1399,6 @@ public final class MapboxMap {
*
* @param polygon Polygon to remove
*/
- @UiThread
public void removePolygon(@NonNull Polygon polygon) {
annotationManager.removeAnnotation(polygon);
}
@@ -1352,7 +1408,6 @@ public final class MapboxMap {
*
* @param annotation The annotation object to remove.
*/
- @UiThread
public void removeAnnotation(@NonNull Annotation annotation) {
annotationManager.removeAnnotation(annotation);
}
@@ -1362,7 +1417,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);
}
@@ -1372,7 +1426,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);
}
@@ -1380,7 +1433,6 @@ public final class MapboxMap {
/**
* Removes all annotations from the map.
*/
- @UiThread
public void removeAnnotations() {
annotationManager.removeAnnotations();
}
@@ -1388,7 +1440,6 @@ public final class MapboxMap {
/**
* Removes all markers, polylines, polygons, overlays, etc from the map.
*/
- @UiThread
public void clear() {
annotationManager.removeAnnotations();
}
@@ -1454,7 +1505,6 @@ 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);
}
@@ -1465,7 +1515,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on a polygon.
* To unset the callback, use null.
*/
- @UiThread
public void setOnPolygonClickListener(@Nullable OnPolygonClickListener listener) {
annotationManager.setOnPolygonClickListener(listener);
}
@@ -1476,7 +1525,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on a polyline.
* To unset the callback, use null.
*/
- @UiThread
public void setOnPolylineClickListener(@Nullable OnPolylineClickListener listener) {
annotationManager.setOnPolylineClickListener(listener);
}
@@ -1491,7 +1539,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");
@@ -1503,7 +1550,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();
}
@@ -1513,7 +1559,6 @@ public final class MapboxMap {
*
* @param marker the marker to deselect
*/
- @UiThread
public void deselectMarker(@NonNull Marker marker) {
annotationManager.deselectMarker(marker);
}
@@ -1523,7 +1568,6 @@ public final class MapboxMap {
*
* @return The currently selected marker.
*/
- @UiThread
public List<Marker> getSelectedMarkers() {
return annotationManager.getSelectedMarkers();
}
@@ -1551,7 +1595,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);
}
@@ -1561,7 +1604,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();
@@ -1572,7 +1614,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);
}
@@ -1582,7 +1623,6 @@ public final class MapboxMap {
*
* @return If true, map allows concurrent multiple infowindows to be shown.
*/
- @UiThread
public boolean isAllowConcurrentMultipleOpenInfoWindows() {
return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows();
}
@@ -1676,7 +1716,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);
@@ -1687,7 +1726,6 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
cameraChangeDispatcher.setOnCameraIdleListener(listener);
}
@@ -1697,7 +1735,6 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener);
}
@@ -1707,7 +1744,6 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
public void setOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.setOnCameraMoveStartedListener(listener);
}
@@ -1717,7 +1753,6 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
cameraChangeDispatcher.setOnCameraMoveListener(listener);
}
@@ -1728,9 +1763,9 @@ public final class MapboxMap {
* @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
@@ -1744,7 +1779,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the map is scrolled.
* To unset the callback, use null.
*/
- @UiThread
public void setOnScrollListener(@Nullable OnScrollListener listener) {
onRegisterTouchListener.onRegisterScrollListener(listener);
}
@@ -1755,7 +1789,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the map is flinged.
* To unset the callback, use null.
*/
- @UiThread
public void setOnFlingListener(@Nullable OnFlingListener listener) {
onRegisterTouchListener.onRegisterFlingListener(listener);
}
@@ -1766,7 +1799,6 @@ 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.
*/
- @UiThread
public void setOnMapClickListener(@Nullable OnMapClickListener listener) {
onRegisterTouchListener.onRegisterMapClickListener(listener);
}
@@ -1777,7 +1809,6 @@ 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.
*/
- @UiThread
public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
onRegisterTouchListener.onRegisterMapLongClickListener(listener);
}
@@ -1788,7 +1819,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);
}
@@ -1798,7 +1828,6 @@ public final class MapboxMap {
*
* @return Current active InfoWindow Click Listener
*/
- @UiThread
public OnInfoWindowClickListener getOnInfoWindowClickListener() {
return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener();
}
@@ -1809,7 +1838,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);
@@ -1838,7 +1866,6 @@ public final class MapboxMap {
*
* @return Current active InfoWindow Close Listener
*/
- @UiThread
public OnInfoWindowCloseListener getOnInfoWindowCloseListener() {
return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener();
}
@@ -1851,8 +1878,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/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public boolean isMyLocationEnabled() {
return trackingSettings.isMyLocationEnabled();
}
@@ -1867,8 +1896,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/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setMyLocationEnabled(boolean enabled) {
trackingSettings.setMyLocationEnabled(enabled);
}
@@ -1877,9 +1908,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/plugins/locationlayer instead.
*/
- @UiThread
@Nullable
+ @Deprecated
public Location getMyLocation() {
return trackingSettings.getMyLocation();
}
@@ -1890,8 +1923,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/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener
listener) {
trackingSettings.setOnMyLocationChangeListener(listener);
@@ -1901,8 +1936,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.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setLocationSource(@Nullable LocationEngine locationSource) {
trackingSettings.setLocationSource(locationSource);
}
@@ -1912,8 +1949,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/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyLocationTrackingModeChangeListener(
@Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) {
trackingSettings.setOnMyLocationTrackingModeChangeListener(listener);
@@ -1924,8 +1963,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/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) {
trackingSettings.setOnMyBearingTrackingModeChangeListener(listener);
}
@@ -1939,7 +1980,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);
}
@@ -1951,7 +1991,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) {
@@ -1966,7 +2005,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,
@@ -1981,7 +2019,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) {
@@ -1996,7 +2033,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,
@@ -2004,6 +2040,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
//
@@ -2102,6 +2147,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)
@@ -2266,7 +2326,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;
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 80b25bf0de..7b979f5563 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
@@ -79,12 +79,12 @@ public class MapboxMapOptions implements Parcelable {
private int[] myLocationBackgroundPadding;
private int myLocationAccuracyTintColor;
private int myLocationAccuracyAlpha;
+ private float myLocationAccuracyThreshold;
+ private boolean prefetchesTiles = true;
+ private boolean zMediaOverlay = false;
private String apiBaseUrl;
- @Deprecated
- private boolean textureMode;
-
private String style;
/**
@@ -148,10 +148,13 @@ 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;
+
+ prefetchesTiles = in.readByte() != 0;
+ zMediaOverlay = in.readByte() != 0;
}
static Bitmap getBitmapFromDrawable(Drawable drawable) {
@@ -291,8 +294,12 @@ public class MapboxMapOptions implements Parcelable {
mapboxMapOptions.myLocationAccuracyTint(
typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor,
ColorUtils.getPrimaryColor(context)));
- mapboxMapOptions.textureMode(
- typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false));
+ mapboxMapOptions.myLocationAccuracyThreshold(
+ typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0));
+ mapboxMapOptions.setPrefetchesTiles(
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true));
+ mapboxMapOptions.renderSurfaceOnTop(
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableZMediaOverlay, false));
} finally {
typedArray.recycle();
}
@@ -681,22 +688,59 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Enable 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.
- * </p>
+ * 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 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
*
- * @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;
+ public MapboxMapOptions setPrefetchesTiles(boolean enable) {
+ this.prefetchesTiles = enable;
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.
@@ -988,22 +1032,21 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Get the current configured debug state for a map view.
+ * Returns current accuracy threshold value (in meters).
*
- * @return True indicates debug is enabled.
+ * @return Value of accuracy threshold (in meters), below which circle won't be displayed
*/
- public boolean getDebugActive() {
- return debugActive;
+ public float getMyLocationAccuracyThreshold() {
+ return myLocationAccuracyThreshold;
}
/**
- * Returns true if TextureView is being used a render view.
+ * Get the current configured debug state for a map view.
*
- * @return True if TextureView is used.
- * @deprecated As of the 4.2.0 release, using TextureView is deprecated.
+ * @return True indicates debug is enabled.
*/
- public boolean getTextureMode() {
- return textureMode;
+ public boolean getDebugActive() {
+ return debugActive;
}
public static final Parcelable.Creator<MapboxMapOptions> CREATOR = new Parcelable.Creator<MapboxMapOptions>() {
@@ -1065,10 +1108,13 @@ public class MapboxMapOptions implements Parcelable {
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));
}
@Override
@@ -1153,6 +1199,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;
}
@@ -1189,6 +1238,13 @@ 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;
+ }
+
return false;
}
@@ -1230,9 +1286,12 @@ 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);
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
new file mode 100644
index 0000000000..072382ce07
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
@@ -0,0 +1,244 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.graphics.RectF;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates {@link Marker}'s functionality.
+ */
+class MarkerContainer implements Markers {
+
+ private final NativeMapView nativeMapView;
+ private final MapView mapView;
+ private final LongSparseArray<Annotation> annotations;
+ private final IconManager iconManager;
+ private final MarkerViewManager markerViewManager;
+
+ MarkerContainer(NativeMapView nativeMapView, MapView mapView, LongSparseArray<Annotation> annotations, IconManager
+ iconManager, MarkerViewManager markerViewManager) {
+ this.nativeMapView = nativeMapView;
+ this.mapView = mapView;
+ this.annotations = annotations;
+ this.iconManager = iconManager;
+ this.markerViewManager = markerViewManager;
+ }
+
+ @Override
+ public Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) {
+ Marker marker = prepareMarker(markerOptions);
+ long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0;
+ marker.setMapboxMap(mapboxMap);
+ marker.setId(id);
+ annotations.put(id, marker);
+ return marker;
+ }
+
+ @Override
+ public List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap
+ mapboxMap) {
+ int count = markerOptionsList.size();
+ List<Marker> markers = new ArrayList<>(count);
+ if (nativeMapView != null && count > 0) {
+ BaseMarkerOptions markerOptions;
+ Marker marker;
+ for (int i = 0; i < count; i++) {
+ markerOptions = markerOptionsList.get(i);
+ marker = prepareMarker(markerOptions);
+ markers.add(marker);
+ }
+
+ if (markers.size() > 0) {
+ 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;
+ }
+
+ @Override
+ public void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) {
+ ensureIconLoaded(updatedMarker, mapboxMap);
+ nativeMapView.updateMarker(updatedMarker);
+ annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker);
+ }
+
+ @Override
+ public List<Marker> obtainAll() {
+ List<Marker> markers = new ArrayList<>();
+ Annotation annotation;
+ for (int i = 0; i < annotations.size(); i++) {
+ annotation = annotations.get(annotations.keyAt(i));
+ if (annotation instanceof Marker) {
+ markers.add((Marker) annotation);
+ }
+ }
+ return 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);
+
+ long[] ids = nativeMapView.queryPointAnnotations(rect);
+
+ List<Long> idsList = new ArrayList<>(ids.length);
+ for (long id : ids) {
+ idsList.add(id);
+ }
+
+ List<Marker> annotations = new ArrayList<>(ids.length);
+ List<Annotation> annotationList = obtainAnnotations();
+ int count = annotationList.size();
+ for (int i = 0; i < count; i++) {
+ Annotation annotation = annotationList.get(i);
+ if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) {
+ annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation);
+ }
+ }
+
+ return new ArrayList<>(annotations);
+ }
+
+ @Override
+ public MarkerView addViewBy(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, @Nullable
+ MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) {
+ final MarkerView marker = prepareViewMarker(markerOptions);
+
+ // add marker to map
+ marker.setMapboxMap(mapboxMap);
+ long id = nativeMapView.addMarker(marker);
+ marker.setId(id);
+ annotations.put(id, marker);
+
+ if (onMarkerViewAddedListener != null) {
+ markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener);
+ }
+ markerViewManager.setEnabled(true);
+ markerViewManager.setWaitingForRenderInvoke(true);
+ return marker;
+ }
+
+ @Override
+ public List<MarkerView> addViewsBy(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, @NonNull
+ MapboxMap mapboxMap) {
+ List<MarkerView> markers = new ArrayList<>();
+ for (BaseMarkerViewOptions markerViewOption : markerViewOptions) {
+ // if last marker
+ if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) {
+ // get notified when render occurs to invalidate and draw MarkerViews
+ markerViewManager.setWaitingForRenderInvoke(true);
+ }
+ // add marker to map
+ MarkerView marker = prepareViewMarker(markerViewOption);
+ marker.setMapboxMap(mapboxMap);
+ long id = nativeMapView.addMarker(marker);
+ marker.setId(id);
+ annotations.put(id, marker);
+ markers.add(marker);
+ }
+ markerViewManager.setEnabled(true);
+ markerViewManager.update();
+ return markers;
+ }
+
+ @Override
+ public List<MarkerView> obtainViewsIn(@NonNull RectF rectangle) {
+ float pixelRatio = nativeMapView.getPixelRatio();
+ RectF rect = new RectF(rectangle.left / pixelRatio,
+ rectangle.top / pixelRatio,
+ rectangle.right / pixelRatio,
+ rectangle.bottom / pixelRatio);
+
+ long[] ids = nativeMapView.queryPointAnnotations(rect);
+
+ List<Long> idsList = new ArrayList<>(ids.length);
+ for (long id : ids) {
+ idsList.add(id);
+ }
+
+ List<MarkerView> annotations = new ArrayList<>(ids.length);
+ List<Annotation> annotationList = obtainAnnotations();
+ int count = annotationList.size();
+ for (int i = 0; i < count; i++) {
+ Annotation annotation = annotationList.get(i);
+ if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) {
+ annotations.add((MarkerView) annotation);
+ }
+ }
+
+ return new ArrayList<>(annotations);
+ }
+
+ @Override
+ public void reload() {
+ iconManager.reloadIcons();
+ int count = annotations.size();
+ for (int i = 0; i < count; i++) {
+ Annotation annotation = annotations.get(i);
+ if (annotation instanceof Marker) {
+ Marker marker = (Marker) annotation;
+ nativeMapView.removeAnnotation(annotation.getId());
+ long newId = nativeMapView.addMarker(marker);
+ marker.setId(newId);
+ }
+ }
+ }
+
+ private Marker prepareMarker(BaseMarkerOptions markerOptions) {
+ Marker marker = markerOptions.getMarker();
+ Icon icon = iconManager.loadIconForMarker(marker);
+ marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon));
+ return marker;
+ }
+
+ private void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) {
+ if (!(marker instanceof MarkerView)) {
+ iconManager.ensureIconLoaded(marker, mapboxMap);
+ }
+ }
+
+ private List<Annotation> obtainAnnotations() {
+ List<Annotation> annotations = new ArrayList<>();
+ for (int i = 0; i < this.annotations.size(); i++) {
+ annotations.add(this.annotations.get(this.annotations.keyAt(i)));
+ }
+ return annotations;
+ }
+
+ private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) {
+ MarkerView marker = markerViewOptions.getMarker();
+ Icon icon = markerViewOptions.getIcon();
+ if (icon == null) {
+ icon = IconFactory.getInstance(mapView.getContext()).defaultMarkerView();
+ }
+ iconManager.loadIconForMarkerView(marker);
+ marker.setIcon(icon);
+ return marker;
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java
new file mode 100644
index 0000000000..d646e0ac49
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java
@@ -0,0 +1,39 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.graphics.RectF;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Marker}'s collection.
+ */
+interface Markers {
+ Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap);
+
+ List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap);
+
+ void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap);
+
+ List<Marker> obtainAll();
+
+ List<Marker> obtainAllIn(@NonNull RectF rectangle);
+
+ MarkerView addViewBy(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap,
+ @Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener);
+
+ List<MarkerView> addViewsBy(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions,
+ @NonNull MapboxMap mapboxMap);
+
+ List<MarkerView> obtainViewsIn(@NonNull RectF rectangle);
+
+ void reload();
+}
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 af3b57151d..3ce6aab581 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,27 +1,25 @@
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.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;
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;
@@ -36,7 +34,6 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
import timber.log.Timber;
@@ -55,53 +52,32 @@ 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;
- //
- // Static methods
- //
-
static {
- System.loadLibrary("mapbox-gl");
+ LibraryLoader.load();
}
//
// 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);
}
//
@@ -110,9 +86,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;
}
@@ -123,60 +100,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) {
@@ -197,41 +126,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) {
@@ -561,6 +467,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;
@@ -596,13 +509,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;
@@ -621,7 +527,7 @@ final class NativeMapView {
if (isDestroyedOn("getMetersPerPixelAtLatitude")) {
return 0;
}
- return nativeGetMetersPerPixelAtLatitude(lat, getZoom());
+ return nativeGetMetersPerPixelAtLatitude(lat, getZoom()) / pixelRatio;
}
public ProjectedMeters projectedMetersForLatLng(LatLng latLng) {
@@ -692,6 +598,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() {
@@ -841,6 +761,13 @@ final class NativeMapView {
nativeRemoveImage(name);
}
+ public Bitmap getImage(String name) {
+ if (isDestroyedOn("getImage")) {
+ return null;
+ }
+ return nativeGetImage(name);
+ }
+
// Feature querying
@NonNull
@@ -872,13 +799,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;
@@ -901,29 +821,10 @@ final class NativeMapView {
// 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) {
@@ -943,33 +844,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();
@@ -1038,6 +919,8 @@ final class NativeMapView {
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);
@@ -1049,8 +932,6 @@ final class NativeMapView {
private native boolean nativeGetDebug();
- private native void nativeSetEnableFps(boolean enable);
-
private native boolean nativeIsFullyLoaded();
private native void nativeSetReachability(boolean status);
@@ -1117,6 +998,8 @@ final class NativeMapView {
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);
@@ -1134,6 +1017,10 @@ final class NativeMapView {
private native Light nativeGetLight();
+ private native void nativeSetPrefetchesTiles(boolean enable);
+
+ private native boolean nativeGetPrefetchesTiles();
+
int getWidth() {
if (isDestroyedOn("")) {
return 0;
@@ -1153,11 +1040,11 @@ final class NativeMapView {
//
void addOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) {
- onMapChangedListeners.add(listener);
+ mapView.addOnMapChangedListener(listener);
}
void removeOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) {
- onMapChangedListeners.remove(listener);
+ mapView.removeOnMapChangedListener(listener);
}
//
@@ -1165,8 +1052,35 @@ 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);
+ }
+
+ });
+ }
+
+ });
+ }
+
+ });
}
}
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
new file mode 100644
index 0000000000..016862bddc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
@@ -0,0 +1,82 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.Polygon;
+import com.mapbox.mapboxsdk.annotations.PolygonOptions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates {@link Polygon}'s functionality.
+ */
+class PolygonContainer implements Polygons {
+
+ private final NativeMapView nativeMapView;
+ private final LongSparseArray<Annotation> annotations;
+
+ PolygonContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+ this.nativeMapView = nativeMapView;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
+ Polygon polygon = polygonOptions.getPolygon();
+ if (!polygon.getPoints().isEmpty()) {
+ long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0;
+ polygon.setId(id);
+ polygon.setMapboxMap(mapboxMap);
+ annotations.put(id, polygon);
+ }
+ return polygon;
+ }
+
+ @Override
+ public List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
+ int count = polygonOptionsList.size();
+
+ Polygon polygon;
+ List<Polygon> polygons = new ArrayList<>(count);
+ if (nativeMapView != null && count > 0) {
+ for (PolygonOptions polygonOptions : polygonOptionsList) {
+ polygon = polygonOptions.getPolygon();
+ if (!polygon.getPoints().isEmpty()) {
+ polygons.add(polygon);
+ }
+ }
+
+ long[] ids = nativeMapView.addPolygons(polygons);
+ for (int i = 0; i < ids.length; i++) {
+ polygon = polygons.get(i);
+ polygon.setMapboxMap(mapboxMap);
+ polygon.setId(ids[i]);
+ annotations.put(ids[i], polygon);
+ }
+ }
+ return polygons;
+ }
+
+ @Override
+ public void update(Polygon polygon) {
+ nativeMapView.updatePolygon(polygon);
+ annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon);
+ }
+
+ @Override
+ public List<Polygon> obtainAll() {
+ List<Polygon> polygons = new ArrayList<>();
+ Annotation annotation;
+ for (int i = 0; i < annotations.size(); i++) {
+ annotation = annotations.get(annotations.keyAt(i));
+ if (annotation instanceof Polygon) {
+ polygons.add((Polygon) annotation);
+ }
+ }
+ return polygons;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java
new file mode 100644
index 0000000000..2a0190b5ba
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java
@@ -0,0 +1,22 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.annotations.Polygon;
+import com.mapbox.mapboxsdk.annotations.PolygonOptions;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Polygon}'s collection.
+ */
+interface Polygons {
+ Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap);
+
+ List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap);
+
+ void update(Polygon polygon);
+
+ List<Polygon> obtainAll();
+}
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
new file mode 100644
index 0000000000..303b25fb55
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
@@ -0,0 +1,81 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.Polyline;
+import com.mapbox.mapboxsdk.annotations.PolylineOptions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates {@link Polyline}'s functionality.
+ */
+class PolylineContainer implements Polylines {
+
+ private final NativeMapView nativeMapView;
+ private final LongSparseArray<Annotation> annotations;
+
+ PolylineContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+ this.nativeMapView = nativeMapView;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
+ Polyline polyline = polylineOptions.getPolyline();
+ if (!polyline.getPoints().isEmpty()) {
+ long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0;
+ polyline.setMapboxMap(mapboxMap);
+ polyline.setId(id);
+ annotations.put(id, polyline);
+ }
+ return polyline;
+ }
+
+ @Override
+ public List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
+ int count = polylineOptionsList.size();
+ Polyline polyline;
+ List<Polyline> polylines = new ArrayList<>(count);
+ if (nativeMapView != null && count > 0) {
+ for (PolylineOptions options : polylineOptionsList) {
+ polyline = options.getPolyline();
+ if (!polyline.getPoints().isEmpty()) {
+ polylines.add(polyline);
+ }
+ }
+
+ 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;
+ }
+
+ @Override
+ public void update(Polyline polyline) {
+ nativeMapView.updatePolyline(polyline);
+ annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline);
+ }
+
+ @Override
+ public List<Polyline> obtainAll() {
+ List<Polyline> polylines = new ArrayList<>();
+ Annotation annotation;
+ for (int i = 0; i < annotations.size(); i++) {
+ annotation = annotations.get(annotations.keyAt(i));
+ if (annotation instanceof Polyline) {
+ polylines.add((Polyline) annotation);
+ }
+ }
+ return polylines;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java
new file mode 100644
index 0000000000..c9a865cdd0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java
@@ -0,0 +1,22 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.annotations.Polyline;
+import com.mapbox.mapboxsdk.annotations.PolylineOptions;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Polyline}'s collection.
+ */
+interface Polylines {
+ Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap);
+
+ List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap);
+
+ void update(Polyline polyline);
+
+ List<Polyline> obtainAll();
+}
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 bd0bf7c83b..6881ca067b 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
@@ -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;
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 d788b7772b..6f63c2eba8 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
@@ -346,7 +346,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);
@@ -358,7 +358,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/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
new file mode 100644
index 0000000000..3f43522e01
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
@@ -0,0 +1,145 @@
+package com.mapbox.mapboxsdk.maps.renderer;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+
+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 class MapRenderer implements GLSurfaceView.Renderer, MapRendererScheduler {
+
+ // Holds the pointer to the native peer after initialisation
+ private long nativePtr = 0;
+
+ private final GLSurfaceView glSurfaceView;
+
+ private MapboxMap.OnFpsChangedListener onFpsChangedListener;
+
+ public MapRenderer(Context context, GLSurfaceView glSurfaceView) {
+ this.glSurfaceView = glSurfaceView;
+
+ FileSource fileSource = FileSource.getInstance(context);
+ float pixelRatio = context.getResources().getDisplayMetrics().density;
+ String programCacheDir = context.getCacheDir().getAbsolutePath();
+
+ // Initialise native peer
+ nativeInitialize(this, fileSource, pixelRatio, programCacheDir);
+ }
+
+ public void setOnFpsChangedListener(MapboxMap.OnFpsChangedListener listener) {
+ onFpsChangedListener = listener;
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ nativeOnSurfaceCreated();
+ }
+
+ @Override
+ public 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);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ nativeRender();
+
+ if (onFpsChangedListener != null) {
+ updateFps();
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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
+ */
+ void queueEvent(MapRendererRunnable runnable) {
+ this.queueEvent((Runnable) runnable);
+ }
+
+ private native void nativeInitialize(MapRenderer self,
+ FileSource fileSource,
+ float pixelRatio,
+ String programCacheDir);
+
+ @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/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
index 2b327409ae..45f72af1c5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
@@ -1,10 +1,8 @@
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;
@@ -13,11 +11,8 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
-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 AppCompatImageView 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 f74286705c..983ba2550f 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;
@@ -48,7 +49,10 @@ import timber.log.Timber;
* <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;
@@ -71,6 +75,7 @@ public class MyLocationView extends View {
private float accuracy;
private Paint accuracyPaint;
+ private float accuracyThreshold;
private ValueAnimator locationChangeAnimator;
private ValueAnimator accuracyAnimator;
@@ -592,6 +597,16 @@ public class MyLocationView extends View {
}
/**
+ * 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
@@ -764,6 +779,7 @@ public class MyLocationView extends View {
locationSource = new WeakReference<>(locationEngine);
}
+ @SuppressLint("MissingPermission")
@Override
public void onConnected() {
MyLocationView locationView = userLocationView.get();
@@ -952,10 +968,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();
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 fe2f18e4dd..a1d5b13b8b 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
@@ -13,7 +13,10 @@ import com.mapbox.mapboxsdk.maps.Projection;
/**
* 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 +54,7 @@ public class MyLocationViewSettings {
//
private int accuracyAlpha;
+ private float accuracyThreshold = 0f;
@ColorInt
private int accuracyTintColor;
@@ -93,6 +97,7 @@ public class MyLocationViewSettings {
setBackgroundTintColor(options.getMyLocationBackgroundTintColor());
setAccuracyAlpha(options.getMyLocationAccuracyAlpha());
setAccuracyTintColor(options.getMyLocationAccuracyTintColor());
+ setAccuracyThreshold(options.getMyLocationAccuracyThreshold());
}
/**
@@ -293,6 +298,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 a1bd98b780..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,6 +22,7 @@ import timber.log.Timber;
* Not public api.
*/
public class ConnectivityReceiver extends BroadcastReceiver {
+ @SuppressLint("StaticFieldLeak")
private static ConnectivityReceiver INSTANCE;
/**
@@ -82,7 +84,7 @@ public class ConnectivityReceiver extends BroadcastReceiver {
@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/net/NativeConnectivityListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java
index 76ce1de9d7..ae74859228 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java
@@ -1,12 +1,14 @@
package com.mapbox.mapboxsdk.net;
+import com.mapbox.mapboxsdk.LibraryLoader;
+
/**
* Updates the native library's connectivity state
*/
class NativeConnectivityListener implements ConnectivityListener {
static {
- System.loadLibrary("mapbox-gl");
+ LibraryLoader.load();
}
private long nativePtr;
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 d572d696db..130284e88d 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,10 +1,12 @@
package com.mapbox.mapboxsdk.offline;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
+import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
@@ -25,7 +27,7 @@ public class OfflineManager {
//
static {
- System.loadLibrary("mapbox-gl");
+ LibraryLoader.load();
}
// Native peer pointer
@@ -39,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
@@ -89,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) {
@@ -106,10 +109,10 @@ 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();
@@ -235,10 +238,11 @@ 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 ee6f8aa87f..f210729037 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
@@ -6,6 +6,7 @@ import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.storage.FileSource;
import java.lang.annotation.Retention;
@@ -23,7 +24,7 @@ public class OfflineRegion {
//
static {
- System.loadLibrary("mapbox-gl");
+ LibraryLoader.load();
}
// Members
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..72df86d80d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
@@ -0,0 +1,260 @@
+package com.mapbox.mapboxsdk.snapshotter;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+
+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.maps.MapboxMap;
+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 {
+
+ /**
+ * Can be used to get notified of errors
+ * in snapshot generation
+ *
+ * @see MapSnapshotter#start(MapboxMap.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_PX = 4;
+
+ // Holds the pointer to JNI NativeMapView
+ private long nativePtr = 0;
+
+ private final Context context;
+ private MapboxMap.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;
+
+ /**
+ * @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;
+ }
+
+ /**
+ * @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,
+ 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 MapboxMap.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 MapboxMap.SnapshotReadyCallback callback, ErrorHandler errorHandler) {
+ if (this.callback != null) {
+ throw new IllegalStateException("Snapshotter was already started");
+ }
+
+ this.callback = callback;
+ this.errorHandler = errorHandler;
+ nativeStart();
+ }
+
+ /**
+ * Must be called in on the thread
+ * the object was created on.
+ */
+ public void cancel() {
+ callback = null;
+ nativeCancel();
+ }
+
+ protected void addOverlay(Bitmap original) {
+ float margin = context.getResources().getDisplayMetrics().density * LOGO_MARGIN_PX;
+ Canvas canvas = new Canvas(original);
+ Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, null);
+ canvas.drawBitmap(logo, margin, original.getHeight() - (logo.getHeight() + margin), null);
+ }
+
+ /**
+ * Called by JNI peer when snapshot is ready.
+ * Always called on the origin (main) thread.
+ *
+ * @param bitmap the generated snapshot
+ */
+ protected void onSnapshotReady(Bitmap bitmap) {
+ if (callback != null) {
+ addOverlay(bitmap);
+ callback.onSnapshotReady(bitmap);
+ 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,
+ 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 eafef80e8d..a968cdf192 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
@@ -72,9 +72,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;
@@ -83,7 +83,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: ");
}
}
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/CircleLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
index 1a7df06031..10663142b5 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
@@ -278,6 +278,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 +421,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/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
index 5e345268f9..8d5858217b 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.
/**
@@ -446,6 +528,27 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface CIRCLE_PITCH_SCALE {}
+ // 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 translation reference point.
/**
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 e4ea9676fa..d4ddbe48ef 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
@@ -322,11 +322,11 @@ public class PropertyFactory {
/**
* Stroke thickness.
*
- * @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>> lineWidth(CameraFunction<Z, Float> function) {
+ public static <T> PropertyValue<Function<T, Float>> lineWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("line-width", function);
}
@@ -953,6 +953,28 @@ public class PropertyFactory {
}
/**
+ * 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 <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>> 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
@@ -1460,11 +1482,11 @@ public class PropertyFactory {
/**
* 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);
}
@@ -1676,7 +1698,7 @@ public class PropertyFactory {
}
/**
- * Scale factor for icon. 1 is original size, 3 triples the size.
+ * 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
@@ -1688,7 +1710,7 @@ public class PropertyFactory {
/**
- * Scale factor for icon. 1 is original size, 3 triples the size.
+ * Scales the original size of the icon by the provided factor. The new pixel size of the image will be the original pixel size multiplied by {@link PropertyFactory#iconSize}. 1 is the original size; 3 triples the size of the image.
*
* @param <T> the function input type
* @param function a wrapper function for Float
@@ -1860,6 +1882,52 @@ 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 <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 <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
@@ -1989,11 +2057,11 @@ public class PropertyFactory {
/**
* 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);
}
@@ -2035,11 +2103,11 @@ public class PropertyFactory {
/**
* 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);
}
@@ -2058,11 +2126,11 @@ public class PropertyFactory {
/**
* 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);
}
@@ -2081,11 +2149,11 @@ public class PropertyFactory {
/**
* 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);
}
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..d0fb82dce5 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
@@ -252,6 +252,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 +911,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/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java
index cb6465a6b1..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
@@ -43,8 +43,7 @@ public class Light {
*
* @return anchor as String
*/
- @Property.ANCHOR
- public String getAnchor() {
+ @Property.ANCHOR public String getAnchor() {
return nativeGetAnchor();
}
@@ -107,7 +106,7 @@ public class Light {
*
* @return color as String
*/
- public String getColor() {
+ public String getColor() {
return nativeGetColor();
}
@@ -143,7 +142,7 @@ public class Light {
*
* @return intensity as Float
*/
- public float getIntensity() {
+ public float getIntensity() {
return nativeGetIntensity();
}
@@ -166,30 +165,17 @@ public class Light {
}
private native void nativeSetAnchor(String anchor);
-
private native String nativeGetAnchor();
-
private native void nativeSetPosition(Position position);
-
private native Position nativeGetPosition();
-
private native TransitionOptions nativeGetPositionTransition();
-
private native void nativeSetPositionTransition(long duration, long delay);
-
private native void nativeSetColor(String color);
-
private native String nativeGetColor();
-
private native TransitionOptions nativeGetColorTransition();
-
private native void nativeSetColorTransition(long duration, long delay);
-
private native void nativeSetIntensity(float intensity);
-
private native float nativeGetIntensity();
-
private native TransitionOptions nativeGetIntensityTransition();
-
private native void nativeSetIntensityTransition(long duration, long delay);
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java
new file mode 100644
index 0000000000..84e5e96fa4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java
@@ -0,0 +1,137 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.v4.content.ContextCompat;
+
+import com.mapbox.mapboxsdk.Mapbox;
+import com.mapbox.mapboxsdk.geometry.LatLngQuad;
+
+import java.net.URL;
+
+
+/**
+ * Image source, allows a georeferenced raster image to be shown on the map.
+ * <p>
+ * The georeferenced image scales and rotates as the user zooms and rotates the map.
+ * The geographic location of the raster image content, supplied with `LatLngQuad`,
+ * can be non-axis aligned.
+ * </p>
+ * * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-image">the style specification</a>
+ */
+@UiThread
+public class ImageSource extends Source {
+
+ /**
+ * Internal use
+ *
+ * @param nativePtr - pointer to native peer
+ */
+ public ImageSource(long nativePtr) {
+ super(nativePtr);
+ }
+
+ /**
+ * Create an ImageSource from coordinates and an image URL
+ *
+ * @param id The source id
+ * @param coordinates The Latitude and Longitude of the four corners of the image
+ * @param url remote json file
+ */
+ public ImageSource(String id, LatLngQuad coordinates, URL url) {
+ initialize(id, coordinates);
+ setUrl(url);
+ }
+
+ /**
+ * Create an ImageSource from coordinates and a bitmap image
+ *
+ * @param id The source id
+ * @param coordinates The Latitude and Longitude of the four corners of the image
+ * @param bitmap A Bitmap image
+ */
+ public ImageSource(String id, LatLngQuad coordinates, @NonNull android.graphics.Bitmap bitmap) {
+ initialize(id, coordinates);
+ setImage(bitmap);
+ }
+
+ /**
+ * Create an ImageSource from coordinates and a bitmap image resource
+ *
+ * @param id The source id
+ * @param coordinates The Latitude and Longitude of the four corners of the image
+ * @param resourceId The resource ID of a Bitmap image
+ */
+ public ImageSource(String id, LatLngQuad coordinates, @DrawableRes int resourceId) {
+ initialize(id, coordinates);
+ setImage(resourceId);
+ }
+
+ /**
+ * Updates the source image url
+ *
+ * @param url An Image url
+ */
+ public void setUrl(URL url) {
+ setUrl(url.toExternalForm());
+ }
+
+ /**
+ * Updates the source image url
+ *
+ * @param url An image url
+ */
+ public void setUrl(String url) {
+ nativeSetUrl(url);
+ }
+
+ /**
+ * Updates the source image to a bitmap
+ *
+ * @param bitmap A Bitmap image
+ */
+ public void setImage(@NonNull android.graphics.Bitmap bitmap) {
+ nativeSetImage(bitmap);
+ }
+
+ /**
+ * Updates the source image to a bitmap image resource
+ *
+ * @param resourceId The resource ID of a Bitmap image
+ */
+ public void setImage(@DrawableRes int resourceId) throws IllegalArgumentException {
+ Context context = Mapbox.getApplicationContext();
+ Drawable drawable = ContextCompat.getDrawable(context, resourceId);
+ if (drawable instanceof BitmapDrawable) {
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ nativeSetImage(bitmapDrawable.getBitmap());
+ } else {
+ throw new IllegalArgumentException("Failed to decode image. The resource provided must be a Bitmap.");
+ }
+ }
+
+ /**
+ * @return The url or null
+ */
+ @Nullable
+ public String getUrl() {
+ return nativeGetUrl();
+ }
+
+ protected native void initialize(String layerId, LatLngQuad payload);
+
+ protected native void nativeSetUrl(String url);
+
+ protected native String nativeGetUrl();
+
+ protected native void nativeSetImage(Bitmap bitmap);
+
+ @Override
+ protected native void finalize() throws Throwable;
+}
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/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
index f30cb7c27a..40045f851f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
@@ -72,19 +72,21 @@
<public name="mapbox_uiAttributionMarginRight" type="attr" />
<public name="mapbox_uiAttributionMarginBottom" type="attr" />
- <!-- Deprecated to 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-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/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_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/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
index e17f01d075..b673224094 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -40,6 +40,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,8 +114,8 @@
<attr name="mapbox_uiAttributionMarginBottom" format="dimension"/>
<attr name="mapbox_uiAttributionTintColor" format="color"/>
- <!-- Deprecated to use TextureView-->
- <attr name="mapbox_renderTextureMode" format="boolean"/>
+ <attr name="mapbox_enableTilePrefetch" format="boolean"/>
+ <attr name="mapbox_enableZMediaOverlay" format="boolean"/>
</declare-styleable>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
index 69ab7568bb..b51c890e5c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
@@ -2,5 +2,4 @@
<resources>
<color name="mapbox_gray">#7D7F80</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/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
index e05190cd57..e05190cd57 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
index 605e159b84..605e159b84 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java
index 1c259af2d0..1c259af2d0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
index 94b629860e..94b629860e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
index fa571e06b1..fa571e06b1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
index ebd30f5422..ebd30f5422 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java
index 3933c68887..3933c68887 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java
index 54bb0e8cf4..54bb0e8cf4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
index 0c5f3a4be2..0c5f3a4be2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java
index aaef7f8a51..cb654aa556 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.model.constants;
+package com.mapbox.mapboxsdk.constants;
public class AppConstant {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
index bb96c9939d..8d9a360714 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
@@ -74,6 +74,15 @@ 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)
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
index 12297247cf..12297247cf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
index 06e93b9d2f..06e93b9d2f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
index 00fd125a1a..00fd125a1a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
index 12b779de5d..12b779de5d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
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
new file mode 100644
index 0000000000..0d592f9bb3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java
@@ -0,0 +1,81 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AnnotationManagerTest {
+
+ @Test
+ public void checksAddAMarker() throws Exception {
+ NativeMapView aNativeMapView = mock(NativeMapView.class);
+ MapView aMapView = mock(MapView.class);
+ LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>();
+ MarkerViewManager aMarkerViewManager = mock(MarkerViewManager.class);
+ IconManager aIconManager = mock(IconManager.class);
+ Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray);
+ Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
+ Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
+ Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
+ AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
+ aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
+ Marker aMarker = mock(Marker.class);
+ long aId = 5L;
+ when(aNativeMapView.addMarker(aMarker)).thenReturn(aId);
+ BaseMarkerOptions aMarkerOptions = mock(BaseMarkerOptions.class);
+ MapboxMap aMapboxMap = mock(MapboxMap.class);
+ when(aMarkerOptions.getMarker()).thenReturn(aMarker);
+
+ annotationManager.addMarker(aMarkerOptions, aMapboxMap);
+
+ assertEquals(aMarker, annotationManager.getAnnotations().get(0));
+ assertEquals(aMarker, annotationManager.getAnnotation(aId));
+ }
+
+ @Test
+ public void checksAddMarkers() throws Exception {
+ NativeMapView aNativeMapView = mock(NativeMapView.class);
+ MapView aMapView = mock(MapView.class);
+ LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>();
+ MarkerViewManager aMarkerViewManager = mock(MarkerViewManager.class);
+ IconManager aIconManager = mock(IconManager.class);
+ Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray);
+ Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
+ Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
+ Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
+ AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
+ aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
+ 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);
+
+ annotationManager.addMarkers(markerList, aMapboxMap);
+
+ assertEquals(2, annotationManager.getAnnotations().size());
+ assertEquals("first", ((Marker) annotationManager.getAnnotations().get(0)).getTitle());
+ assertEquals("second", ((Marker) annotationManager.getAnnotations().get(1)).getTitle());
+ assertEquals("first", ((Marker) annotationManager.getAnnotation(firstId)).getTitle());
+ assertEquals("second", ((Marker) annotationManager.getAnnotation(secondId)).getTitle());
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/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/MapboxGLAndroidSDKTestApp/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/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
index de5f364a5b..de5f364a5b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
index fbe00b4dce..fbe00b4dce 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
index c9ce19dc85..c9ce19dc85 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
index 933bf05b39..933bf05b39 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
index bac1154d62..bac1154d62 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
index 94a6dc2194..94a6dc2194 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
index dd4c7b25ee..dd4c7b25ee 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
index ca6ee9cea8..ca6ee9cea8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
+++ b/platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
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 56b537e2a2..67939b5144 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -25,15 +25,11 @@ android {
}
lintOptions {
+ baseline file("lint-baseline-local.xml")
checkAllWarnings true
warningsAsErrors true
- disable 'MissingTranslation'
- disable 'IconDensities'
- disable 'InvalidPackage'
- }
-
- testOptions {
- unitTests.returnDefaultValues = true
+ disable 'MissingTranslation', 'GoogleAppIndexingWarning', 'UnpackedNativeCode', 'IconDipSize', 'TypographyQuotes'
+ abortOnError false
}
buildTypes {
@@ -67,7 +63,6 @@ dependencies {
// Leak Canary
debugCompile rootProject.ext.dep.leakCanaryDebug
releaseCompile rootProject.ext.dep.leakCanaryRelease
- testCompile rootProject.ext.dep.leakCanaryTest
// Mapbox Android Services (Java component)
compile(rootProject.ext.dep.mapboxJavaServices) {
@@ -75,8 +70,6 @@ dependencies {
}
// Testing dependencies
- testCompile rootProject.ext.dep.junit
- testCompile rootProject.ext.dep.mockito
androidTestCompile rootProject.ext.dep.testSpoonRunner
androidTestCompile rootProject.ext.dep.supportAnnotations
androidTestCompile rootProject.ext.dep.testRunner
@@ -90,5 +83,6 @@ 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-config.gradle b/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle
index 1068e5e69e..8346806633 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle
@@ -5,14 +5,15 @@
task accessToken {
def tokenFile = new File("${projectDir}/src/main/res/values/developer-config.xml")
if (!tokenFile.exists()) {
+ String mapboxAccessToken = "$System.env.MAPBOX_ACCESS_TOKEN"
+ if (mapboxAccessToken == "null") {
+ System.out.println("You should set the MAPBOX_ACCESS_TOKEN environment variable.")
+ mapboxAccessToken = "YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE"
+ }
String tokenFileContents = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<resources>\n" +
- " <string name=\"mapbox_access_token\">" + "$System.env.MAPBOX_ACCESS_TOKEN" + "</string>\n" +
+ " <string name=\"mapbox_access_token\">" + mapboxAccessToken + "</string>\n" +
"</resources>"
-
- if (tokenFileContents == null) {
- throw new InvalidUserDataException("You must set the MAPBOX_ACCESS_TOKEN environment variable.")
- }
tokenFile.write(tokenFileContents)
}
}
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..294d57bce1 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
@@ -779,6 +779,22 @@ public class MapboxMapTest extends BaseActivityTest {
}));
}
+ // Tile pre-fetching
+
+ @Test
+ public void testTilePrefetch() {
+ validateTestSetup();
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
+ @Override
+ public void onViewAction(UiController uiController, View view) {
+ mapboxMap.setPrefetchesTiles(true);
+ assertTrue(mapboxMap.getPrefetchesTiles());
+ mapboxMap.setPrefetchesTiles(false);
+ assertFalse(mapboxMap.getPrefetchesTiles());
+ }
+ }));
+ }
+
private class MapboxMapAction implements ViewAction {
private InvokeViewAction invokeViewAction;
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/maps/widgets/MyLocationViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java
index fa19235ad0..ec7105c321 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;
@@ -97,6 +98,7 @@ public class MyLocationViewTest extends BaseActivityTest {
return getClass().getSimpleName();
}
+ @SuppressLint("MissingPermission")
@Override
public void perform(UiController uiController, View view) {
if (isEnabled) {
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 c8f9640433..559e446307 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
@@ -1048,6 +1048,54 @@ public class CircleLayerTest extends BaseActivityTest {
}
@Test
+ public void testCirclePitchAlignmentAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("circle-pitch-alignment");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getCirclePitchAlignment().getValue(), (String) CIRCLE_PITCH_ALIGNMENT_MAP);
+ }
+ });
+ }
+
+ @Test
+ public void testCirclePitchAlignmentAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("circle-pitch-alignment");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ circlePitchAlignment(
+ zoom(
+ interval(
+ stop(2, circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getCirclePitchAlignment());
+ assertNotNull(layer.getCirclePitchAlignment().getFunction());
+ assertEquals(CameraFunction.class, layer.getCirclePitchAlignment().getFunction().getClass());
+ assertEquals(IntervalStops.class, layer.getCirclePitchAlignment().getFunction().getStops().getClass());
+ assertEquals(1, ((IntervalStops) layer.getCirclePitchAlignment().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
public void testCircleStrokeWidthTransition() {
validateTestSetup();
setupLayer();
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..60cf4ced3d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java
@@ -0,0 +1,54 @@
+package com.mapbox.mapboxsdk.testapp.style;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.test.espresso.UiController;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+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, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ Drawable drawable = rule.getActivity().getResources().getDrawable(R.drawable.ic_launcher_round);
+ assertTrue(drawable instanceof BitmapDrawable);
+
+ 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/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java
index 595a2e43dc..8123d24be8 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
@@ -204,6 +204,63 @@ public class LineLayerTest extends BaseActivityTest {
}
@Test
+ public void testLineJoinAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-join");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ lineJoin(property("FeaturePropertyA", Stops.<String>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getLineJoin());
+ assertNotNull(layer.getLineJoin().getFunction());
+ assertEquals(SourceFunction.class, layer.getLineJoin().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineJoin().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getLineJoin().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testLineJoinAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-join");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ lineJoin(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, lineJoin(LINE_JOIN_BEVEL))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getLineJoin());
+ assertNotNull(layer.getLineJoin().getFunction());
+ assertEquals(SourceFunction.class, layer.getLineJoin().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineJoin().getFunction()).getProperty());
+ assertEquals(IntervalStops.class, layer.getLineJoin().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
public void testLineMiterLimitAsConstant() {
validateTestSetup();
setupLayer();
@@ -861,6 +918,139 @@ public class LineLayerTest extends BaseActivityTest {
}
@Test
+ public void testLineWidthAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ lineWidth(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getLineWidth());
+ assertNotNull(layer.getLineWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getLineWidth().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testLineWidthAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ lineWidth(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, lineWidth(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getLineWidth());
+ assertNotNull(layer.getLineWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testLineWidthAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ lineWidth(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, lineWidth(0.3f))
+ )
+ ).withDefaultValue(lineWidth(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getLineWidth());
+ assertNotNull(layer.getLineWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getLineWidth().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue());
+ }
+ });
+
+ }
+
+ @Test
+ public void testLineWidthAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ lineWidth(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, lineWidth(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(lineWidth(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getLineWidth());
+ assertNotNull(layer.getLineWidth().getFunction());
+ assertEquals(CompositeFunction.class, layer.getLineWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineWidth().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getLineWidth().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineWidth().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+ });
+ }
+
+ @Test
public void testLineGapWidthTransition() {
validateTestSetup();
setupLayer();
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 b0854f4a47..f8248ae4a7 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
@@ -1214,6 +1214,159 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testIconAnchorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(iconAnchor(ICON_ANCHOR_CENTER));
+ assertEquals((String) layer.getIconAnchor().getValue(), (String) ICON_ANCHOR_CENTER);
+ }
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconAnchor(
+ zoom(
+ interval(
+ stop(2, iconAnchor(ICON_ANCHOR_CENTER))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getIconAnchor());
+ assertNotNull(layer.getIconAnchor().getFunction());
+ assertEquals(CameraFunction.class, layer.getIconAnchor().getFunction().getClass());
+ assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
+ assertEquals(1, ((IntervalStops) layer.getIconAnchor().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconAnchor(property("FeaturePropertyA", Stops.<String>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getIconAnchor());
+ assertNotNull(layer.getIconAnchor().getFunction());
+ assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconAnchor(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, iconAnchor(ICON_ANCHOR_CENTER))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getIconAnchor());
+ assertNotNull(layer.getIconAnchor().getFunction());
+ assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty());
+ assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testIconPitchAlignmentAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-pitch-alignment");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getIconPitchAlignment().getValue(), (String) ICON_PITCH_ALIGNMENT_MAP);
+ }
+ });
+ }
+
+ @Test
+ public void testIconPitchAlignmentAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-pitch-alignment");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconPitchAlignment(
+ zoom(
+ interval(
+ stop(2, iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getIconPitchAlignment());
+ assertNotNull(layer.getIconPitchAlignment().getFunction());
+ assertEquals(CameraFunction.class, layer.getIconPitchAlignment().getFunction().getClass());
+ assertEquals(IntervalStops.class, layer.getIconPitchAlignment().getFunction().getStops().getClass());
+ assertEquals(1, ((IntervalStops) layer.getIconPitchAlignment().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
public void testTextPitchAlignmentAsConstant() {
validateTestSetup();
setupLayer();
@@ -1694,6 +1847,139 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextMaxWidthAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextMaxWidthAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, textMaxWidth(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextMaxWidthAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textMaxWidth(0.3f))
+ )
+ ).withDefaultValue(textMaxWidth(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue());
+ }
+ });
+
+ }
+
+ @Test
+ public void testTextMaxWidthAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, textMaxWidth(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(textMaxWidth(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(CompositeFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getTextMaxWidth().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextMaxWidth().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+ });
+ }
+
+ @Test
public void testTextLineHeightAsConstant() {
validateTestSetup();
setupLayer();
@@ -1792,6 +2078,139 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextLetterSpacingAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextLetterSpacingAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, textLetterSpacing(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextLetterSpacingAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textLetterSpacing(0.3f))
+ )
+ ).withDefaultValue(textLetterSpacing(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue());
+ }
+ });
+
+ }
+
+ @Test
+ public void testTextLetterSpacingAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, textLetterSpacing(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(textLetterSpacing(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(CompositeFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getTextLetterSpacing().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextLetterSpacing().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+ });
+ }
+
+ @Test
public void testTextJustifyAsConstant() {
validateTestSetup();
setupLayer();
@@ -1840,6 +2259,63 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextJustifyAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-justify");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textJustify(property("FeaturePropertyA", Stops.<String>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextJustify());
+ assertNotNull(layer.getTextJustify().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextJustify().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextJustify().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextJustify().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextJustifyAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-justify");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textJustify(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, textJustify(TEXT_JUSTIFY_LEFT))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextJustify());
+ assertNotNull(layer.getTextJustify().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextJustify().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextJustify().getFunction()).getProperty());
+ assertEquals(IntervalStops.class, layer.getTextJustify().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
public void testTextAnchorAsConstant() {
validateTestSetup();
setupLayer();
@@ -1888,6 +2364,63 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextAnchorAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textAnchor(property("FeaturePropertyA", Stops.<String>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextAnchor());
+ assertNotNull(layer.getTextAnchor().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextAnchor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextAnchor().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextAnchor().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextAnchorAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textAnchor(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, textAnchor(TEXT_ANCHOR_CENTER))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextAnchor());
+ assertNotNull(layer.getTextAnchor().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextAnchor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextAnchor().getFunction()).getProperty());
+ assertEquals(IntervalStops.class, layer.getTextAnchor().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
public void testTextMaxAngleAsConstant() {
validateTestSetup();
setupLayer();
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..1c4981ca5e 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,28 @@ 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;
+ private Activity activity;
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 +34,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 +42,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();
+ }
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 1a70e4548a..bf97749b9e 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"
@@ -358,6 +367,16 @@
android:name="android.support.PARENT_ACTIVITY"
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.maplayout.DoubleMapActivity"
android:description="@string/description_doublemap"
@@ -392,10 +411,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 +433,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"/>
@@ -535,6 +564,17 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
+ <activity
+ android:name=".activity.style.AnimatedImageSourceActivity"
+ android:label="@string/activity_animated_image_source"
+ android:description="@string/description_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>
<!-- Features -->
<activity
@@ -593,12 +633,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"/>
@@ -649,7 +700,13 @@
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>
<!-- For Instrumentation tests -->
<activity
@@ -678,4 +735,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 e344343627..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
@@ -2,8 +2,10 @@ package com.mapbox.mapboxsdk.testapp;
import android.app.Application;
import android.os.StrictMode;
+import android.text.TextUtils;
import com.mapbox.mapboxsdk.Mapbox;
+import com.mapbox.mapboxsdk.testapp.utils.TokenUtils;
import com.squareup.leakcanary.LeakCanary;
import timber.log.Timber;
@@ -18,6 +20,12 @@ import static timber.log.Timber.DebugTree;
*/
public class MapboxApplication extends Application {
+ 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 "
+ + "automatically include it in the Test App. Otherwise, you can manually include it in the "
+ + "res/values/developer-config.xml file in the MapboxGLAndroidSDKTestApp folder.";
+
@Override
public void onCreate() {
super.onCreate();
@@ -43,7 +51,12 @@ public class MapboxApplication extends Application {
.penaltyDeath()
.build());
- Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token));
+ String mapboxAccessToken = TokenUtils.getMapboxAccessToken(getApplicationContext());
+ if (TextUtils.isEmpty(mapboxAccessToken) || mapboxAccessToken.equals(DEFAULT_MAPBOX_ACCESS_TOKEN)) {
+ Timber.e(ACCESS_TOKEN_NOT_SET_MESSAGE);
+ }
+
+ Mapbox.getInstance(getApplicationContext(), mapboxAccessToken);
}
private void initializeLogger() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
index f8617366a0..3f20f19f5d 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
@@ -92,7 +92,7 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis
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");
}
}
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/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
index 8b238e49a8..50adeb2d74 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
@@ -274,7 +274,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
String json = GeoParseUtil.loadStringFromAssets(activity.getApplicationContext(), "points.geojson");
return GeoParseUtil.parseGeoJsonCoordinates(json);
} catch (IOException | JSONException exception) {
- Timber.e("Could not add markers,", exception);
+ Timber.e(exception, "Could not add markers");
return null;
}
}
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..61ece0a94f 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
@@ -36,6 +36,7 @@ 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;
/**
@@ -150,7 +151,11 @@ public class MarkerViewActivity extends AppCompatActivity {
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());
+ viewCountView.setText(String.format(
+ Locale.getDefault(),
+ getString(R.string.viewcache_size),
+ markerViewContainer.getChildCount())
+ );
}
}
}
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 fecfe2a842..49c9663672 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
@@ -57,8 +57,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()
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..cc44ac9715
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
@@ -0,0 +1,174 @@
+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.view.animation.FastOutLinearInInterpolator;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.animation.AnticipateOvershootInterpolator;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+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 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;
+ findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ view.setVisibility(View.GONE);
+ createAnimator(mapboxMap.getCameraPosition()).start();
+ }
+ });
+ }
+
+ private Animator createAnimator(CameraPosition currentPosition) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.play(createLatLngAnimator(currentPosition.target));
+ animatorSet.play(createZoomAnimator(currentPosition.zoom));
+ animatorSet.play(createBearingAnimator(currentPosition.bearing));
+ animatorSet.play(createTiltAnimator(currentPosition.tilt));
+ return animatorSet;
+ }
+
+ private Animator createLatLngAnimator(LatLng currentPosition) {
+ LatLng target = new LatLng(37.789992, -122.402214);
+ ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, target);
+ latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ latLngAnimator.setInterpolator(new FastOutSlowInInterpolator());
+ latLngAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setLatLng((LatLng) animation.getAnimatedValue());
+ }
+ });
+ return latLngAnimator;
+ }
+
+ private Animator createZoomAnimator(double currentZoom) {
+ ValueAnimator zoomAnimator = ValueAnimator.ofFloat((float) currentZoom, 14.5f);
+ zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR));
+ zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR));
+ zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator());
+ zoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setZoom((Float) animation.getAnimatedValue());
+ }
+ });
+ return zoomAnimator;
+ }
+
+ private Animator createBearingAnimator(double currentBearing) {
+ ValueAnimator bearingAnimator = ValueAnimator.ofFloat((float) currentBearing, 135);
+ bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR));
+ bearingAnimator.setInterpolator(new FastOutLinearInInterpolator());
+ bearingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setBearing((Float) animation.getAnimatedValue());
+ }
+ });
+ return bearingAnimator;
+ }
+
+ private Animator createTiltAnimator(double currentTilt) {
+ ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, 60);
+ tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR));
+ tiltAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setTilt((Float) animation.getAnimatedValue());
+ }
+ });
+ return tiltAnimator;
+ }
+
+ 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;
+ }
+ }
+
+ @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/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
index 10464b087a..2820bdbd53 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
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.camera;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
@@ -24,6 +25,9 @@ import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
+/**
+ * Test activity showcasing how to listen to camera change events.
+ */
public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback {
private MapView mapView;
@@ -83,6 +87,7 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary));
fab.setOnClickListener(new View.OnClickListener() {
+ @SuppressLint("InflateParams")
@Override
public void onClick(View view) {
Context context = view.getContext();
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..e3a9551b8e 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,122 @@
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(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(final MapboxMap 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 +144,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 +156,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/MaxMinZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
index 014743df96..53a5800f26 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
@@ -13,6 +13,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;
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..1b49e9e3d6 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
@@ -69,6 +69,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..20174daeaa 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
@@ -48,7 +48,7 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity {
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<Feature> features = mapboxMap.queryRenderedFeatures(box);
// Show count
@@ -66,22 +66,21 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity {
}
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..480e48437a 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
@@ -46,6 +46,15 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
@Override
public void onMapReady(final MapboxMap mapboxMap) {
QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap;
+
+ // Add layer / source
+ final GeoJsonSource source = new GeoJsonSource("highlighted-shapes-source");
+ mapboxMap.addSource(source);
+ mapboxMap.addLayer(
+ new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source")
+ .withProperties(fillColor(Color.RED))
+ );
+
selectionBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -53,7 +62,7 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
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));
+ Timber.i("Querying box %s for buildings", box);
List<Feature> features = mapboxMap.queryRenderedFeatures(box, Filter.lt("height", 10), "building");
// Show count
@@ -62,17 +71,8 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
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)));
+ // 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..220137d07d 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,7 +3,6 @@ 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;
@@ -14,15 +13,10 @@ 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;
@@ -57,9 +51,10 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi
// Add a symbol layer (also works with annotations)
try {
- mapboxMap.addSource(new GeoJsonSource("symbols-source", readRawResource(R.raw.test_points_utrecht)));
+ mapboxMap.addSource(new GeoJsonSource("symbols-source", ResourceUtils.readRawResource(
+ QueryRenderedFeaturesBoxSymbolCountActivity.this, R.raw.test_points_utrecht)));
} catch (IOException ioException) {
- Timber.e("Could not load geojson: " + ioException.getMessage());
+ Timber.e(ioException, "Could not load geojson");
return;
}
mapboxMap.addImage(
@@ -76,7 +71,7 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi
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<Feature> features = mapboxMap.queryRenderedFeatures(box, "symbols-layer");
// Show count
@@ -94,23 +89,6 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi
});
}
- 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..1bd6a34b7c 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
@@ -59,9 +59,9 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
public void onMapClick(@NonNull LatLng point) {
// Query
final PointF pixel = mapboxMap.getProjection().toScreenLocation(point);
- Timber.i(String.format(
+ Timber.i(
"Requesting features for %sx%s (%sx%s adjusted for density)",
- pixel.x, pixel.y, pixel.x / density, pixel.y / density)
+ pixel.x, pixel.y, pixel.x / density, pixel.y / density
);
List<Feature> features = mapboxMap.queryRenderedFeatures(pixel);
@@ -84,22 +84,21 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
}
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 +184,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 +200,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/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..bbdf450505 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);
}
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..655012f799 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
@@ -15,6 +15,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.
*/
@@ -56,7 +58,7 @@ public class SnapshotActivity extends AppCompatActivity implements OnMapReadyCal
snapshotView.setImageBitmap(snapshot);
Toast.makeText(
SnapshotActivity.this,
- String.format("Snapshot taken in %d ms", duration),
+ 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..7fa4792a38 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
@@ -3,7 +3,6 @@ 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;
@@ -21,16 +20,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 +44,6 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
@Override
public void onMapReady(MapboxMap map) {
-
mapboxMap = map;
// Add info window adapter
@@ -64,27 +64,32 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
double distanceKm = marker.getPosition().distanceTo(point) / 1000;
// Get the info window
- InfoWindow infoWindow = marker.getInfoWindow();
+ 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) infoWindow.getView()).setText(String.format("%.2fkm", distanceKm));
-
- // Update the info window position (as the text length changes)
- infoWindow.update();
+ TextView textView = (TextView) infoWindow.getView();
+ textView.setText(String.format(Locale.getDefault(), "%.2fkm", distanceKm));
+ textView.post(new Runnable() {
+ @Override
+ public void run() {
+ // Update the info window position (as the text length changes)
+ infoWindow.update();
+ }
+ });
}
}
});
// 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()))
));
@@ -94,15 +99,13 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
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.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..22347f8a92 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
@@ -18,6 +18,9 @@ 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;
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..2c83f6d908
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
@@ -0,0 +1,288 @@
+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.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;
+ private int mapViewCounter;
+
+ @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);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ findViewById(R.id.fabFragment).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ addMapFragment();
+ }
+ });
+
+ findViewById(R.id.fabBottomSheet).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View 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() {
+ super.onBackPressed();
+ if (mapViewCounter > 0) {
+ mapViewCounter--;
+ Toast.makeText(this, "Amount of main map fragments: " + mapViewCounter, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void addMapFragment() {
+ FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
+ MainMapFragment mainMapFragment = MainMapFragment.newInstance(mapViewCounter);
+ if (mapViewCounter == 0) {
+ fragmentTransaction.add(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT);
+ } else {
+ fragmentTransaction.replace(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT);
+ }
+ fragmentTransaction.addToBackStack(null);
+ fragmentTransaction.commit();
+ mapViewCounter++;
+ Toast.makeText(this, "Amount of main map fragments: " + mapViewCounter, 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..6134b4cb7a 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,10 +1,19 @@
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.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
import android.widget.TextView;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@@ -12,18 +21,25 @@ 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,49 +48,128 @@ 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(new MapView.OnMapChangedListener() {
+ @Override
+ public void onMapChanged(int change) {
+ if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) {
+ Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson());
+ setupNavigationView(mapboxMap.getLayers());
+ }
+ }
+ });
+
mapView.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;
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ mapboxMap.getUiSettings().setZoomControlsEnabled(true);
- mapboxMap.getUiSettings().setZoomControlsEnabled(true);
+ setupNavigationView(mapboxMap.getLayers());
+ setupZoomView();
+ setFpsView();
+ }
- // 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));
- }
- });
+ private void setFpsView() {
+ final TextView fpsView = (TextView) findViewById(R.id.fpsView);
+ mapboxMap.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() {
+ @Override
+ public void onFpsChanged(double fps) {
+ fpsView.setText(String.format(Locale.US,"FPS: %4.2f", fps));
}
});
+ }
+
+ private void setupNavigationView(List<Layer> layerList) {
+ final LayerListAdapter adapter = new LayerListAdapter(this, layerList);
+ ListView listView = (ListView) findViewById(R.id.listView);
+ listView.setAdapter(adapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Layer clickedLayer = adapter.getItem(position);
+ toggleLayerVisibility(clickedLayer);
+ closeNavigationView();
+ }
+ });
+ }
+ 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(new MapboxMap.OnCameraChangeListener() {
+ @Override
+ public void onCameraChange(CameraPosition 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());
+ 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
@@ -96,6 +191,11 @@ public class DebugModeActivity extends AppCompatActivity {
}
@Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
+ }
+
+ @Override
protected void onStart() {
super.onStart();
mapView.onStart();
@@ -136,4 +236,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..462c0c8025 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
@@ -8,7 +8,6 @@ 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;
@@ -23,6 +22,12 @@ 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 +50,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 +63,9 @@ public class DoubleMapActivity extends AppCompatActivity {
}
}
+ /**
+ * Custom fragment containing 2 MapViews.
+ */
public static class DoubleMapFragment extends Fragment {
private DoubleMapActivity activity;
@@ -107,6 +115,7 @@ public class DoubleMapActivity extends AppCompatActivity {
uiSettings.setLogoEnabled(false);
try {
+ mapboxMap.setMyLocationEnabled(true);
TrackingSettings settings = mapboxMap.getTrackingSettings();
settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
} catch (SecurityException securityException) {
@@ -123,9 +132,6 @@ public class DoubleMapActivity extends AppCompatActivity {
});
}
});
-
- SurfaceView surfaceViewMini = (SurfaceView) mapViewMini.findViewById(R.id.surfaceView);
- surfaceViewMini.setZOrderMediaOverlay(true);
}
@Override
@@ -157,8 +163,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/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..32344248bc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
@@ -0,0 +1,111 @@
+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.maps.OnMapReadyCallback;
+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(new MapView.OnMapChangedListener() {
+ @Override
+ public void onMapChanged(int change) {
+ Timber.e("OnMapChange: %s, %s", change, mapChangeMap.get(change));
+ }
+ });
+
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(55.754020, 37.620948), 12), 9000);
+ }
+ });
+ }
+
+ 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..f0827c65cc 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;
@@ -65,6 +67,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 +108,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/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/SimpleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java
index badb6718cf..8f8a5af3cc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java
@@ -1,18 +1,11 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
-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.Projection;
import com.mapbox.mapboxsdk.testapp.R;
-import timber.log.Timber;
-
/**
* Test activity showcasing a simple MapView without any MapboxMap interaction.
*/
@@ -27,19 +20,6 @@ public class SimpleMapActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- final Projection projection = mapboxMap.getProjection();
-
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- Timber.e(projection.getVisibleRegion().toString());
- }
- });
- }
- });
}
@Override
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..393abb7cd4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java
@@ -0,0 +1,146 @@
+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.maps.OnMapReadyCallback;
+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(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap 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/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
index 344e9e140a..5bffd4d930 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
@@ -12,7 +12,6 @@ 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;
@@ -75,8 +74,7 @@ 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);
@@ -207,7 +205,7 @@ public class OfflineActivity extends AppCompatActivity
@Override
public void onError(String error) {
- Timber.e("Error: " + error);
+ Timber.e("Error: %s" , error);
}
});
}
@@ -223,7 +221,7 @@ public class OfflineActivity extends AppCompatActivity
}
// Start progress bar
- Timber.d("Download started: " + regionName);
+ Timber.d("Download started: %s", regionName);
startProgress();
// Definition
@@ -241,14 +239,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);
}
});
}
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..6b1cb920fc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
@@ -0,0 +1,131 @@
+package com.mapbox.mapboxsdk.testapp.activity.snapshot;
+
+import android.graphics.Bitmap;
+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.maps.MapboxMap;
+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(new MapboxMap.SnapshotReadyCallback() {
+ @Override
+ public void onSnapshotReady(Bitmap snapshot) {
+ Timber.i("Got the snapshot");
+ ImageView imageView = new ImageView(MapSnapshotterActivity.this);
+ imageView.setImageBitmap(snapshot);
+ 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/style/AnimatedImageSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java
new file mode 100644
index 0000000000..1beba632b0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java
@@ -0,0 +1,148 @@
+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;
+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;
+
+/**
+ * Test activity showing how to use a series of images to create an animation
+ * with an ImageSource
+ * <p>
+ * GL-native equivalent of https://www.mapbox.com/mapbox-gl-js/example/animate-images/
+ * </p>
+ */
+public class AnimatedImageSourceActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private static final String ID_IMAGE_SOURCE = "animated_image_source";
+ private static final String ID_IMAGE_LAYER = "animated_image_layer";
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ private Handler handler;
+ private Runnable runnable;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_animated_image_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
+ LatLngQuad quad = new LatLngQuad(
+ new LatLng(46.437, -80.425),
+ new LatLng(46.437, -71.516),
+ new LatLng(37.936, -71.516),
+ new LatLng(37.936, -80.425));
+ mapboxMap.addSource(new ImageSource(ID_IMAGE_SOURCE, quad, R.drawable.southeast_radar_0));
+
+ // add layer
+ RasterLayer layer = new RasterLayer(ID_IMAGE_LAYER, ID_IMAGE_SOURCE);
+ mapboxMap.addLayer(layer);
+
+ // loop refresh geojson
+ handler = new Handler();
+ runnable = new RefreshImageRunnable(mapboxMap, handler);
+ handler.postDelayed(runnable, 100);
+ }
+
+ @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();
+ handler.removeCallbacks(runnable);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ private static class RefreshImageRunnable implements Runnable {
+
+ private MapboxMap mapboxMap;
+ private Handler handler;
+ 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 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;
+ }
+
+ @Override
+ public void run() {
+ ((ImageSource) mapboxMap.getSource(ID_IMAGE_SOURCE)).setImage(drawables[drawableIndex++]);
+ if (drawableIndex > 3) {
+ drawableIndex = 0;
+ }
+ handler.postDelayed(this, 1000);
+ }
+ }
+}
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..f99a1fdb96 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
@@ -74,7 +74,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 +132,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... ");
}
}
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..571b48daa6 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,7 +2,6 @@ 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;
@@ -18,14 +17,9 @@ 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;
@@ -355,7 +349,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(
@@ -375,21 +369,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/FillExtrusionStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
index 1ff0b0e8e1..24914fcbb2 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,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
-
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
@@ -9,6 +8,9 @@ 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;
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..a2111bc304 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
@@ -128,7 +128,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/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..adce889007 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;
@@ -32,16 +31,11 @@ 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;
@@ -285,7 +279,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 +311,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 +326,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(
@@ -477,33 +471,16 @@ public class RuntimeStyleActivity extends AppCompatActivity {
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());
+ Timber.d("Fill color base: %s", stops.getBase());
+ Timber.d("Fill color #stops: %s", stops.size());
if (function.getStops() != null) {
for (Stop<Float, String> stop : stops) {
- Timber.d("Fill color #stops: " + stop);
+ Timber.d("Fill color #stops: %s", 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() {
// Add a source
Source source = new VectorSource("custom-tile-source", new TileSet("2.1.0", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.mvt?api_key=vector-tiles-LM25tq4"));
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..49015ec1e9 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,9 +1,9 @@
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;
@@ -14,22 +14,18 @@ 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 {
@@ -48,13 +44,21 @@ public class StyleFileActivity extends AppCompatActivity {
public void onMapReady(@NonNull final MapboxMap map) {
mapboxMap = map;
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_file);
fab.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- Timber.i("Loading style file");
- new CreateStyleFileTask().execute();
+ new CreateStyleFileTask(view.getContext(), mapboxMap).execute();
+ }
+ });
+
+ FloatingActionButton fabStyleJson = (FloatingActionButton) findViewById(R.id.fab_style_json);
+ fabStyleJson.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
+ fabStyleJson.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new LoadStyleFileTask(view.getContext(), mapboxMap).execute();
}
});
}
@@ -62,44 +66,74 @@ public class StyleFileActivity extends AppCompatActivity {
}
/**
+ * 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..0eaccfef0c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
@@ -0,0 +1,216 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v7.app.AppCompatActivity;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+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.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.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.List;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+
+/**
+ * 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_VALUE = "name_sort";
+
+ 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(MapboxMap map) {
+ mapboxMap = map;
+ try {
+ // read local geojson from raw folder
+ String tinyCountriesJson = ResourceUtils.readRawResource(this, 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);
+
+ // add a geojson to the map
+ Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+
+ // for each feature add a symbolLayer
+ for (Feature feature : featureCollection.getFeatures()) {
+ String countryName = feature.getStringProperty(FEATURE_ID);
+
+ // create View
+ TextView textView = new TextView(this);
+ textView.setBackgroundColor(getResources().getColor(R.color.blueAccent));
+ textView.setPadding(10, 5, 10, 5);
+ textView.setTextColor(Color.WHITE);
+ textView.setText(countryName);
+
+ // create bitmap from view
+ mapboxMap.addImage(countryName, SymbolGenerator.generate(textView));
+ }
+
+ // create layer use
+ mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
+ .withProperties(
+ iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
+ iconAllowOverlap(false)
+ )
+ );
+
+ addSymbolClickListener();
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
+ }
+
+ private void addSymbolClickListener() {
+ mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
+ List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
+ if (!features.isEmpty()) {
+ Feature feature = features.get(0);
+ Timber.v("Feature was clicked with data: %s", feature.toJson());
+ Toast.makeText(
+ SymbolGeneratorActivity.this,
+ "hello from: " + feature.getStringProperty(FEATURE_VALUE),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ });
+ }
+
+ @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 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;
+ }
+ }
+}
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..cd741a85b6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
@@ -0,0 +1,171 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+
+import 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.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;
+
+/**
+ * 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;
+
+ @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(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap map) {
+ mapboxMap = map;
+ updateSource(false);
+ addLayer();
+ addMapClickListener();
+ }
+ });
+ }
+
+ private void updateSource(boolean selected) {
+ FeatureCollection featureCollection = createFeatureCollection(selected);
+ if (source != null) {
+ source.setGeoJson(featureCollection);
+ } else {
+ source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+ }
+ }
+
+ private FeatureCollection createFeatureCollection(boolean selected) {
+ Point point = Point.fromCoordinates(Position.fromCoordinates(-74.016181, 40.701745));
+ Feature feature = Feature.fromGeometry(point);
+ JsonObject properties = new JsonObject();
+ properties.addProperty(KEY_PROPERTY_SELECTED, selected);
+ feature.setProperties(properties);
+ return FeatureCollection.fromFeatures(new Feature[] {feature});
+ }
+
+ private void addLayer() {
+ SymbolLayer 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(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
+ List<Feature> featureList = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
+ if (!featureList.isEmpty()) {
+ Feature feature = featureList.get(0);
+ boolean isSelected = feature.getBooleanProperty(KEY_PROPERTY_SELECTED);
+ updateSource(!isSelected);
+ } else {
+ Timber.e("No features found");
+ }
+ }
+ });
+ }
+
+ @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/userlocation/BaseLocationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
index f41e5e38f0..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
@@ -14,6 +14,9 @@ import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
import java.util.List;
+/**
+ * Base class for location aware activities.
+ */
public abstract class BaseLocationActivity extends AppCompatActivity implements PermissionsListener {
private PermissionsManager permissionsManager;
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 b0ea9c608b..e954b73f8d 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
@@ -12,6 +12,9 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+/**
+ * Test activity showcasing using a custom location engine.
+ */
public class CustomLocationEngineActivity extends BaseLocationActivity {
private MapView mapView;
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 69e6d64325..9f6f2b2fcd 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
@@ -50,7 +50,7 @@ public class MyLocationDrawableActivity extends BaseLocationActivity implements
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);
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 d465d676f7..718c10c7cb 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
@@ -11,6 +11,9 @@ import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
+/**
+ * Test activity showcasing toggling the user location on the map.
+ */
public class MyLocationToggleActivity extends BaseLocationActivity {
private MapView mapView;
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/OfflineListRegionsDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
index f717daeada..76f07ba526 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
@@ -32,7 +32,7 @@ public class OfflineListRegionsDialog extends DialogFragment {
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- Timber.d("Selected item: " + which);
+ Timber.d("Selected item: %s", which);
}
})
.setPositiveButton("Accept", new DialogInterface.OnClickListener() {
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..6220dc7e69 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
@@ -29,7 +29,7 @@ public class OfflineUtils {
String json = jsonObject.toString();
metadata = json.getBytes(JSON_CHARSET);
} catch (Exception exception) {
- Timber.e("Failed to encode metadata: " + exception.getMessage());
+ Timber.e(exception, "Failed to encode metadata: ");
}
return metadata;
}
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/utils/TokenUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TokenUtils.java
new file mode 100644
index 0000000000..e08fdb9154
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TokenUtils.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.utils;
+
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.Mapbox;
+
+public class TokenUtils {
+
+ /**
+ * <p>
+ * Returns the Mapbox access token set in the app resources.
+ * </p>
+ * It will first search for a token in the Mapbox object. If not found it
+ * will then attempt to load the access token from the
+ * {@code res/values/dev.xml} development file.
+ *
+ * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}.
+ * @return The Mapbox access token or null if not found.
+ */
+ public static String getMapboxAccessToken(@NonNull Context context) {
+ try {
+ // Read out AndroidManifest
+ String token = Mapbox.getAccessToken();
+ if (token == null || token.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return token;
+ } catch (Exception exception) {
+ // Use fallback on string resource, used for development
+ int tokenResId = context.getResources()
+ .getIdentifier("mapbox_access_token", "string", context.getPackageName());
+ return tokenResId != 0 ? context.getString(tokenResId) : null;
+ }
+ }
+}
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-mdpi/southeast_radar_0.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_0.png
new file mode 100644
index 0000000000..c304b619c4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_0.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.png
new file mode 100644
index 0000000000..ed09fffbe1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.png
new file mode 100644
index 0000000000..fee630f863
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.png
new file mode 100644
index 0000000000..c4c7146afa
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.png
Binary files differ
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_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
new file mode 100644
index 0000000000..26b40b9ab6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_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_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..d4933bfb9a
--- /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.774913"
+ app:mapbox_cameraTargetLng="-122.419368"
+ 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_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_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_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_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_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_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_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..168361a263
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml
@@ -0,0 +1,8 @@
+<?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"/>
+</menu>
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_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..865baede0e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
@@ -0,0 +1,95 @@
+<?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="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..9ade28ae8d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml
@@ -0,0 +1,17 @@
+<?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>
+</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..4d1f7eac38
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -0,0 +1,65 @@
+<?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">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_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_camera_animator">Use Android SDK Animators to animate camera position changes</string>
+ <string name="description_symbol_generator">Use Android SDK Views as symbols</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 74833105a4..15a916fac9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -1,181 +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>
-
- <!--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>
-
- <!--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>
- <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>
-
- <!--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..8f394d0eb4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
@@ -0,0 +1,65 @@
+<?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_camera_animator">Animator animation</string>
+ <string name="activity_symbol_generator">SymbolGenerator</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/activity/SimpleWearMapActivity.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java
deleted file mode 100644
index f5bca0e051..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.mapbox.weartestapp.activity;
-
-import android.os.Bundle;
-import android.support.wearable.activity.WearableActivity;
-
-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 {
-
- private MapView mapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_simple_mapview);
-
- 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.
- }
- });
- }
-
- @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/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 7c28433d96..bd95bdf7fa 100644
--- a/platform/android/README.md
+++ b/platform/android/README.md
@@ -1,6 +1,6 @@
# [Mapbox Android SDK](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.
@@ -40,13 +40,16 @@ These dependencies are required for all operating systems and all platform targe
- 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
+ - g++-4.9 or later
- [cURL](https://curl.haxx.se) (for build only)
- [Node.js](https://nodejs.org/) or later (for build only)
- [pkg-config](https://wiki.freedesktop.org/www/Software/pkg-config/) (for build only)
+**Note**: We partially support C++14 because GCC 4.9 does not fully implement the
+final draft of the C++14 standard. More information in [DEVELOPING.md](DEVELOPING.md).
+
##### Additional Dependencies for Linux
_These instructions were tested on Ubuntu 16.04 LTS (aka Xenial Xerus)._
@@ -75,6 +78,10 @@ make aproj
Open Android Studio project in `/platform/android`, run `make android-configuration` in the root folder of the project.
+##### Setup Checkstyle
+
+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.
##### Setting Mapbox Access Token
@@ -87,3 +94,8 @@ With the first gradle invocation, gradle will take the value of the `MAPBOX_ACCE
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`.
More information about building and distributing this project in [DISTRIBUTE.md][https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/DISTRIBUTE.md].
+
+#### Symbolicating native crashes
+
+When hitting native crashes you can use ndk-stack to symbolicate crashes.
+More information in [this](https://github.com/mapbox/mapbox-gl-native/wiki/Getting-line-numbers-from-an-Android-crash-with-ndk-stack) guide. \ No newline at end of file
diff --git a/platform/android/bitrise.yml b/platform/android/bitrise.yml
deleted file mode 100644
index dcd4d4fb50..0000000000
--- a/platform/android/bitrise.yml
+++ /dev/null
@@ -1,235 +0,0 @@
----
-format_version: 1.0.0
-default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
-trigger_map:
-- pattern: devicefarmUpload
- workflow: devicefarmUpload
-- pattern: scheduled
- workflow: scheduled
-- pattern: nightly-release
- workflow: nightly-release
-- pattern: "*"
- workflow: primary
-workflows:
- primary:
- steps:
- - script:
- title: Build libmapbox-gl.so for armeabi-v7a
- inputs:
- - content: |-
- #!/bin/bash
- echo "Compile libmapbox-gl.so for armeabi-v7a abi:"
- ccache -z
- BUILDTYPE=Debug make android-lib-arm-v7
- ccache -s
- - script:
- title: Compile Core tests
- inputs:
- - content: |-
- #!/bin/bash
- echo "Compiling core tests:"
- ccache -z
- BUILDTYPE=Debug make android-test-lib-arm-v7
- ccache -s
- - script:
- title: Run local JVM Unit tests on phone module
- inputs:
- - content: |-
- #!/bin/bash
- echo "Running unit tests from MapboxGLAndroidSDKTestApp/src/test:"
- make run-android-unit-test
- - script:
- title: Run local JVM Unit tests on wear module
- inputs:
- - content: |-
- #!/bin/bash
- echo "Running unit tests from MapboxGLAndroidSDKWearTestApp/src/test:"
- make run-android-wear-unit-test
- - script:
- title: Generate Espresso sanity tests
- inputs:
- - content: |-
- #!/bin/bash
- echo "Generate these test locally by executing:"
- make test-code-android
- - script:
- title: Run Checkstyle
- inputs:
- - content: |-
- #!/bin/bash
- # Run checkstyle gradle task on all modules
- make android-checkstyle
- - script:
- title: Run Firebase instrumentation tests
- run_if: '{{getenv "BITRISEIO_GCLOUD_SERVICE_ACCOUNT_JSON_URL" | ne ""}}'
- inputs:
- - content: |-
- #!/bin/bash
- set -euo pipefail
- echo "Downloading Google Cloud authentication:"
- wget -O secret.json "$BITRISEIO_GCLOUD_SERVICE_ACCOUNT_JSON_URL"
-
- echo "Downloading Mapbox accesstoken for running tests:"
- wget -O platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml "$BITRISEIO_TEST_ACCESS_TOKEN_UI_TEST_URL"
-
- echo "Build seperate test apk:"
- ccache -z
- make android-ui-test-arm-v7
- ccache -s
-
- echo "Run tests on firebase:"
- gcloud auth activate-service-account --key-file secret.json --project android-gl-native
- gcloud beta test android devices list
- gcloud beta 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.AttributionTest"
- - script:
- title: Download Firebase results
- is_always_run: true
- run_if: '{{getenv "BITRISEIO_GCLOUD_SERVICE_ACCOUNT_JSON_URL" | ne ""}}'
- inputs:
- - content: |-
- #!/bin/bash
- set -euo pipefail
- mkdir -p platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
-
- echo "The details from Firebase will be downloaded, zipped and attached as a build artefact."
- testUri=$(gsutil ls "gs://test-lab-wrrntqk05p31w-h3y1qk44vuunw/" | tail -n1)
- echo "Downloading from : "$testUri
- gsutil -m cp -R -Z $testUri platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
-
- echo "Try running ndk-stack on downloaded logcat to symbolicate the stacktraces:"
- find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk -type f -name "logcat" -print0 | xargs -0 -Imylogcat mv -i mylogcat ./
- cat logcat | ndk-stack -sym build/android-arm-v7/Debug
- - deploy-to-bitrise-io:
- inputs:
- - deploy_path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
- - is_compress: 'true'
- - notify_user_groups: none
- - slack:
- title: Post to Slack
- inputs:
- - webhook_url: "$SLACK_HOOK_URL"
- - channel: "#gl-bots"
- - from_username: 'Bitrise Android'
- - from_username_on_error: 'Bitrise Android'
- - 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
- scheduled:
- steps:
- - script:
- title: Download maven credentials
- inputs:
- - content: |-
- #!/bin/bash
- aws s3 cp s3://mapbox/android/signing-credentials/secring.gpg platform/android/MapboxGLAndroidSDK/secring.gpg
-
- # Add maven credentals for publishing
- echo "NEXUS_USERNAME=$PUBLISH_NEXUS_USERNAME
- NEXUS_PASSWORD=$PUBLISH_NEXUS_PASSWORD
- signing.keyId=$SIGNING_KEYID
- signing.password=$SIGNING_PASSWORD
- signing.secretKeyRingFile=secring.gpg" >> platform/android/MapboxGLAndroidSDK/gradle.properties
- - script:
- title: Build release
- inputs:
- - content: |-
- #!/bin/bash
- echo "Compile libmapbox-gl.so for all supportd abi's:"
- BUILDTYPE=Release make android-lib-arm-v5
- BUILDTYPE=Release make android-lib-arm-v7
- BUILDTYPE=Release make android-lib-arm-v8
- BUILDTYPE=Release make android-lib-x86
- BUILDTYPE=Release make android-lib-mips
- BUILDTYPE=Release make android-lib-mips-64
- cd platform/android && ./gradlew -Pmapbox.abis=armeabi-v7a MapboxGLAndroidSDK:assembleRelease
- - script:
- title: Publish to maven
- inputs:
- - content: |-
- #!/bin/bash
- echo "Upload aar file to maven:"
- make run-android-upload-archives
- - slack:
- title: Post to Slack
- inputs:
- - webhook_url: "$SLACK_HOOK_URL"
- - channel: "#gl-bots"
- - from_username: 'Bitrise Android'
- - from_username_on_error: 'Bitrise Android'
- - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> Publish of nightly Android SDK SNAPSHOT to maven succeeded.'
- - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> Publish of nightly Android SDK SNAPSHOT to maven failed. @android_team.'
- - 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
- devicefarmUpload:
- steps:
- - script:
- title: Build release
- inputs:
- - content: |-
- #!/bin/bash
- echo "Compile libmapbox-gl.so for all supportd abi's:"
- export BUILDTYPE=Release
- make apackage
- - script:
- title: Add AWS credentials
- inputs:
- - content: |-
- #!/bin/bash
- echo "AWS_ACCESS_KEY_ID_DEVICE_FARM=$AWS_ACCESS_KEY_ID_DEVICE_FARM" >> platform/android/MapboxGLAndroidSDKTestApp/gradle.properties
- echo "AWS_SECRET_ACCESS_KEY_DEVICE_FARM=$AWS_SECRET_ACCESS_KEY_DEVICE_FARM" >> platform/android/MapboxGLAndroidSDKTestApp/gradle.properties
- - script:
- title: Generate sanity tests
- inputs:
- - content: |-
- #!/bin/bash
- echo "Generate these test locally by executing:"
- make test-code-android
- - script:
- title: Run AWS Device Farm instrumentation tests
- inputs:
- - content: |-
- #!/bin/bash
- echo "Run tests on device farm:"
- make run-android-ui-test-aws
- - slack:
- title: Post to Slack
- inputs:
- - webhook_url: "$SLACK_HOOK_URL"
- - channel: "#gl-bots"
- - from_username: 'Bitrise Android'
- - from_username_on_error: 'Bitrise Android'
- - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> for devicefarmUpload passed'
- - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> for devicefarmUpload 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:
- steps:
- - 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 release
- inputs:
- - content: |-
- #!/bin/bash
- echo "Compile libmapbox-gl.so for all supportd abi's:"
- export BUILDTYPE=Release
- make apackage
- - script:
- title: Log metrics
- inputs:
- - content: |-
- #!/bin/bash
- echo "Log binary size metrics to AWS CloudWatch:"
- CLOUDWATCH=true platform/android/scripts/metrics.sh
diff --git a/platform/android/build.gradle b/platform/android/build.gradle
index bc90896812..e298b84da8 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -12,6 +12,7 @@ buildscript {
allprojects {
repositories {
jcenter()
+ maven { url 'https://maven.google.com' }
maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
}
}
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index 84591d644b..227334e0b5 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -55,6 +55,7 @@ macro(mbgl_platform_core)
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,6 +71,24 @@ 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
+ 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
+ PRIVATE platform/linux/src/headless_display_egl.cpp
+ PRIVATE platform/android/src/snapshotter/map_snapshotter.cpp
+ PRIVATE platform/android/src/snapshotter/map_snapshotter.hpp
)
target_include_directories(mbgl-core
@@ -158,6 +177,8 @@ add_library(mbgl-android STATIC
platform/android/src/style/sources/unknown_source.hpp
platform/android/src/style/sources/vector_source.cpp
platform/android/src/style/sources/vector_source.hpp
+ platform/android/src/style/sources/image_source.hpp
+ platform/android/src/style/sources/image_source.cpp
platform/android/src/style/functions/stop.cpp
platform/android/src/style/functions/stop.hpp
platform/android/src/style/functions/categorical_stops.cpp
@@ -184,6 +205,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
@@ -222,6 +247,8 @@ add_library(mbgl-android STATIC
platform/android/src/geometry/lat_lng.hpp
platform/android/src/geometry/lat_lng_bounds.cpp
platform/android/src/geometry/lat_lng_bounds.hpp
+ platform/android/src/geometry/lat_lng_quad.cpp
+ platform/android/src/geometry/lat_lng_quad.hpp
platform/android/src/geometry/projected_meters.cpp
platform/android/src/geometry/projected_meters.hpp
@@ -299,10 +326,10 @@ macro(mbgl_platform_test)
platform/android/src/test/main.jni.cpp
# Headless view
+ platform/default/mbgl/gl/headless_frontend.cpp
+ platform/default/mbgl/gl/headless_frontend.hpp
platform/default/mbgl/gl/headless_backend.cpp
platform/default/mbgl/gl/headless_backend.hpp
- platform/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
diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle
index eb74acac51..521084559f 100644
--- a/platform/android/dependencies.gradle
+++ b/platform/android/dependencies.gradle
@@ -4,15 +4,14 @@ ext {
compileSdkVersion = 25
buildToolsVersion = "25.0.2"
- versionCode = 12
- versionName = "5.1.4"
+ versionCode = 11
+ versionName = "5.0.0"
mapboxServicesVersion = "2.2.3"
- supportLibVersion = "25.3.1"
- wearableVersion = '2.0.0'
- espressoVersion = '2.2.2'
- testRunnerVersion = '0.5'
- leakCanaryVersion = '1.5'
+ supportLibVersion = "25.4.0"
+ espressoVersion = '3.0.1'
+ testRunnerVersion = '1.0.1'
+ leakCanaryVersion = '1.5.1'
dep = [
// mapbox
@@ -25,10 +24,10 @@ ext {
// 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',
+ testSpoonRunner : 'com.squareup.spoon:spoon-client:1.7.1',
testRunner : "com.android.support.test:runner:${testRunnerVersion}",
testRules : "com.android.support.test:rules:${testRunnerVersion}",
testEspressoCore : "com.android.support.test.espresso:espresso-core:${espressoVersion}",
@@ -41,15 +40,10 @@ ext {
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.8.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/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js
index a8b2c98c86..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';
@@ -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/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..9e49915650
--- /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::initializeExtension(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..c5c552459f
--- /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 initializeExtension(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..b80e23e21f
--- /dev/null
+++ b/platform/android/src/android_renderer_frontend.cpp
@@ -0,0 +1,117 @@
+#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();
+}
+
+} // 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..94508fd816
--- /dev/null
+++ b/platform/android/src/android_renderer_frontend.hpp
@@ -0,0 +1,49 @@
+#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;
+
+ // Memory
+ void onLowMemory();
+
+private:
+ MapRenderer& mapRenderer;
+ util::RunLoop* mapRunLoop;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/asset_manager_file_source.cpp b/platform/android/src/asset_manager_file_source.cpp
index 6a3113d696..aa65e3ff48 100644
--- a/platform/android/src/asset_manager_file_source.cpp
+++ b/platform/android/src/asset_manager_file_source.cpp
@@ -1,5 +1,6 @@
#include "asset_manager_file_source.hpp"
+#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/thread.hpp>
@@ -12,10 +13,10 @@ namespace mbgl {
class AssetManagerFileSource::Impl {
public:
- Impl(AAssetManager* assetManager_) : assetManager(assetManager_) {
+ Impl(ActorRef<Impl>, AAssetManager* assetManager_) : assetManager(assetManager_) {
}
- void request(const std::string& url, FileSource::Callback callback) {
+ void request(const std::string& url, ActorRef<FileSourceRequest> req) {
// Note: AssetManager already prepends "assets" to the filename.
const std::string path = mbgl::util::percentDecode(url.substr(8));
@@ -30,7 +31,7 @@ public:
"Could not read asset");
}
- callback(response);
+ req.invoke(&FileSourceRequest::setResponse, response);
}
private:
@@ -39,15 +40,18 @@ private:
AssetManagerFileSource::AssetManagerFileSource(jni::JNIEnv& env, jni::Object<android::AssetManager> assetManager_)
: assetManager(assetManager_.NewGlobalRef(env)),
- thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{"AssetManagerFileSource", util::ThreadPriority::Low},
- AAssetManager_fromJava(&env, jni::Unwrap(**assetManager)))) {
+ impl(std::make_unique<util::Thread<Impl>>("AssetManagerFileSource",
+ AAssetManager_fromJava(&env, jni::Unwrap(**assetManager)))) {
}
AssetManagerFileSource::~AssetManagerFileSource() = default;
std::unique_ptr<AsyncRequest> AssetManagerFileSource::request(const Resource& resource, Callback callback) {
- return thread->invokeWithCallback(&Impl::request, resource.url, callback);
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
+
+ impl->actor().invoke(&Impl::request, resource.url, req->actor());
+
+ return std::move(req);
}
} // namespace mbgl
diff --git a/platform/android/src/asset_manager_file_source.hpp b/platform/android/src/asset_manager_file_source.hpp
index 7a447a2c61..dcad95664a 100644
--- a/platform/android/src/asset_manager_file_source.hpp
+++ b/platform/android/src/asset_manager_file_source.hpp
@@ -20,9 +20,10 @@ public:
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
private:
- jni::UniqueObject<android::AssetManager> assetManager;
class Impl;
- std::unique_ptr<util::Thread<Impl>> thread;
+
+ jni::UniqueObject<android::AssetManager> assetManager;
+ std::unique_ptr<util::Thread<Impl>> impl;
};
} // namespace mbgl
diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp
index 2a0b710f73..f1c72eb5dd 100644
--- a/platform/android/src/conversion/constant.hpp
+++ b/platform/android/src/conversion/constant.hpp
@@ -3,6 +3,7 @@
#include "conversion.hpp"
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/color.hpp>
#include <jni/jni.hpp>
#include <string>
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 16c09b7b52..262e3d3c6a 100644
--- a/platform/android/src/file_source.cpp
+++ b/platform/android/src/file_source.cpp
@@ -1,5 +1,8 @@
#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 "asset_manager_file_source.hpp"
@@ -42,21 +45,22 @@ void FileSource::setAPIBaseUrl(jni::JNIEnv& env, jni::String url) {
void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> transformCallback) {
if (transformCallback) {
- // Launch transformCallback
- fileSource->setResourceTransform([
+ 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.
// Note: we're converting it to shared_ptr because this lambda is converted to a std::function,
// which requires copyability of its captured variables.
- callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter())
- ](mbgl::Resource::Kind kind, std::string&& url_) {
- android::UniqueEnv _env = android::AttachEnv();
- return FileSource::ResourceTransformCallback::onURL(*_env, jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_);
- });
+ [callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter())]
+ (mbgl::Resource::Kind kind, const std::string&& url_) {
+ android::UniqueEnv _env = android::AttachEnv();
+ return FileSource::ResourceTransformCallback::onURL(*_env, jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_);
+ });
+ fileSource->setResourceTransform(resourceTransform->self());
} else {
// Reset the callback
- fileSource->setResourceTransform(nullptr);
+ resourceTransform.reset();
+ fileSource->setResourceTransform({});
}
}
@@ -106,4 +110,4 @@ std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni::
}
} // namespace android
-} // namespace mbgl \ No newline at end of file
+} // namespace mbgl
diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp
index 55e70f34d9..4abe352bff 100644
--- a/platform/android/src/file_source.hpp
+++ b/platform/android/src/file_source.hpp
@@ -7,6 +7,10 @@
#include <jni/jni.hpp>
namespace mbgl {
+
+template <typename T> class Actor;
+class ResourceTransform;
+
namespace android {
/**
@@ -46,10 +50,10 @@ public:
static void registerNative(jni::JNIEnv&);
private:
-
+ std::unique_ptr<Actor<ResourceTransform>> resourceTransform;
std::unique_ptr<mbgl::DefaultFileSource> fileSource;
};
} // namespace android
-} // namespace mbgl \ No newline at end of file
+} // namespace mbgl
diff --git a/platform/android/src/geometry/lat_lng_quad.cpp b/platform/android/src/geometry/lat_lng_quad.cpp
new file mode 100644
index 0000000000..2b36139e18
--- /dev/null
+++ b/platform/android/src/geometry/lat_lng_quad.cpp
@@ -0,0 +1,39 @@
+#include "lat_lng_quad.hpp"
+#include "lat_lng.hpp"
+
+namespace mbgl {
+namespace android {
+
+jni::Object<LatLngQuad> LatLngQuad::New(jni::JNIEnv& env, std::array<mbgl::LatLng, 4> coordinates) {
+ static auto quadConstructor = LatLngQuad::javaClass.GetConstructor<jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>>(env);
+ return LatLngQuad::javaClass.New(env, quadConstructor,
+ LatLng::New(env, coordinates[0]),
+ LatLng::New(env, coordinates[1]),
+ LatLng::New(env, coordinates[2]),
+ LatLng::New(env, coordinates[3]));
+}
+
+std::array<mbgl::LatLng, 4> LatLngQuad::getLatLngArray(jni::JNIEnv& env, jni::Object<LatLngQuad> quad) {
+ static auto topLeftField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "topLeft");
+ static auto topRightField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "topRight");
+ static auto bottomRightField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "bottomRight");
+ static auto bottomLeftField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "bottomLeft");
+
+ return std::array < mbgl::LatLng, 4 > {{
+ LatLng::getLatLng(env, quad.Get(env, topLeftField)),
+ LatLng::getLatLng(env, quad.Get(env, topRightField)),
+ LatLng::getLatLng(env, quad.Get(env, bottomRightField)),
+ LatLng::getLatLng(env, quad.Get(env, bottomLeftField))
+ }};
+}
+
+void LatLngQuad::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ LatLngQuad::javaClass = *jni::Class<LatLngQuad>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<LatLngQuad> LatLngQuad::javaClass;
+
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geometry/lat_lng_quad.hpp b/platform/android/src/geometry/lat_lng_quad.hpp
new file mode 100644
index 0000000000..8f8c9abeef
--- /dev/null
+++ b/platform/android/src/geometry/lat_lng_quad.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/geometry.hpp>
+
+#include <jni/jni.hpp>
+#include <array>
+
+namespace mbgl {
+namespace android {
+
+class LatLngQuad : private mbgl::util::noncopyable {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLngQuad"; };
+
+ static jni::Object<LatLngQuad> New(jni::JNIEnv&, std::array<mbgl::LatLng, 4>);
+
+ static std::array<mbgl::LatLng, 4> getLatLngArray(jni::JNIEnv&, jni::Object<LatLngQuad>);
+
+ static jni::Class<LatLngQuad> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+};
+
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
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 6c490fad5c..6acb6a3664 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -23,6 +23,7 @@
#include "geojson/position.hpp"
#include "geometry/lat_lng.hpp"
#include "geometry/lat_lng_bounds.hpp"
+#include "geometry/lat_lng_quad.hpp"
#include "geometry/projected_meters.hpp"
#include "graphics/pointf.hpp"
#include "graphics/rectf.hpp"
@@ -31,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"
@@ -46,6 +49,7 @@
#include "style/layers/layers.hpp"
#include "style/sources/sources.hpp"
#include "style/light.hpp"
+#include "snapshotter/map_snapshotter.hpp"
namespace mbgl {
namespace android {
@@ -127,6 +131,7 @@ void registerNatives(JavaVM *vm) {
// Geometry
LatLng::registerNative(env);
LatLngBounds::registerNative(env);
+ LatLngQuad::registerNative(env);
ProjectedMeters::registerNative(env);
// GSon
@@ -141,6 +146,8 @@ void registerNatives(JavaVM *vm) {
Polyline::registerNative(env);
// Map
+ MapRenderer::registerNative(env);
+ MapRendererRunnable::registerNative(env);
NativeMapView::registerNative(env);
// Http
@@ -175,6 +182,9 @@ void registerNatives(JavaVM *vm) {
OfflineTilePyramidRegionDefinition::registerNative(env);
OfflineRegionError::registerNative(env);
OfflineRegionStatus::registerNative(env);
+
+ // Snapshotter
+ MapSnapshotter::registerNative(env);
}
} // namespace android
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_renderer.cpp b/platform/android/src/map_renderer.cpp
new file mode 100644
index 0000000000..7655455210
--- /dev/null
+++ b/platform/android/src/map_renderer.cpp
@@ -0,0 +1,200 @@
+#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_)
+ : 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_))
+ , 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 and schedule it on the gl thread
+ android::UniqueEnv _env = android::AttachEnv();
+ auto runnable = std::make_unique<MapRendererRunnable>(*_env, std::move(scheduled));
+
+ static auto queueEvent = javaClass.GetMethod<void(
+ jni::Object<MapRendererRunnable>)>(*_env, "queueEvent");
+ javaPeer->Call(*_env, queueEvent, runnable->getPeer());
+
+ // Release the object 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 intialization 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);
+ 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>,
+ "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..0d614912a9
--- /dev/null
+++ b/platform/android/src/map_renderer.hpp
@@ -0,0 +1,126 @@
+#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);
+
+ ~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;
+
+ 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..df8cba5e55
--- /dev/null
+++ b/platform/android/src/map_renderer_runnable.cpp
@@ -0,0 +1,49 @@
+#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
+ 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 = SeizeGenericWeakRef(env, jni::Object<MapRendererRunnable>(jni::NewWeakGlobalRef(env, instance.Get()).release()));
+}
+
+MapRendererRunnable::~MapRendererRunnable() = default;
+
+void MapRendererRunnable::run(jni::JNIEnv&) {
+ Mailbox::maybeReceive(mailbox);
+}
+
+jni::Object<MapRendererRunnable> MapRendererRunnable::getPeer() {
+ return *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..75646a442d
--- /dev/null
+++ b/platform/android/src/map_renderer_runnable.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include <memory>
+#include <utility>
+
+#include <jni/jni.hpp>
+
+#include "jni/generic_global_ref_deleter.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&);
+
+ jni::Object<MapRendererRunnable> getPeer();
+
+private:
+ GenericUniqueWeakObject<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 159ba70508..a9ed6d5ead 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>
@@ -25,8 +23,10 @@
#include <mbgl/util/logging.hpp>
#include <mbgl/util/platform.hpp>
#include <mbgl/util/projection.hpp>
+#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"
@@ -41,12 +41,16 @@
#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 "style/light.hpp"
+#include "bitmap_factory.hpp"
namespace mbgl {
namespace android {
@@ -54,15 +58,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) {
@@ -70,116 +71,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);
- setViewportSize(getFramebufferSize());
-}
-
-/**
- * From mbgl::Backend.
- */
-gl::ProcAddress NativeMapView::initializeExtension(const char* name) {
- return eglGetProcAddress(name);
-}
-
-void NativeMapView::activate() {
-
- 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() {
- 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
*/
@@ -257,98 +172,26 @@ 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) {
- setViewportSize(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) {
- return jni::Make<jni::String>(env, map->getStyleURL());
+ return jni::Make<jni::String>(env, map->getStyle().getURL());
}
void NativeMapView::setStyleUrl(jni::JNIEnv& env, jni::String url) {
- map->setStyleURL(jni::Make<std::string>(env, url));
+ map->getStyle().loadURL(jni::Make<std::string>(env, url));
}
jni::String NativeMapView::getStyleJson(jni::JNIEnv& env) {
- return jni::Make<jni::String>(env, map->getStyleJSON());
+ return jni::Make<jni::String>(env, map->getStyle().getJSON());
}
void NativeMapView::setStyleJson(jni::JNIEnv& env, jni::String json) {
- map->setStyleJSON(jni::Make<std::string>(env, json));
+ map->getStyle().loadJSON(jni::Make<std::string>(env, json));
}
void NativeMapView::setLatLngBounds(jni::JNIEnv& env, jni::Object<mbgl::android::LatLngBounds> jBounds) {
@@ -480,7 +323,6 @@ void NativeMapView::resetZoom(jni::JNIEnv&) {
void NativeMapView::setMinZoom(jni::JNIEnv&, jni::jdouble zoom) {
map->setMinZoom(zoom);
- recalculateSourceTileCacheSize();
}
jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) {
@@ -489,7 +331,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&) {
@@ -554,11 +395,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) {
@@ -599,7 +444,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;
@@ -608,12 +453,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&) {
@@ -729,8 +572,13 @@ void NativeMapView::addAnnotationIcon(JNIEnv& env, jni::String symbol, jint w, j
}
jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
- map->addAnnotationImage(symbolName,
- std::make_unique<mbgl::style::Image>(std::move(premultipliedImage), float(scale)));
+ map->addAnnotationImage(std::make_unique<mbgl::style::Image>(
+ 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) {
@@ -738,25 +586,25 @@ jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::S
}
jlong NativeMapView::getTransitionDuration(JNIEnv&) {
- const auto transitionOptions = map->getTransitionOptions();
+ const auto transitionOptions = map->getStyle().getTransitionOptions();
return std::chrono::duration_cast<std::chrono::milliseconds>(transitionOptions.duration.value_or(mbgl::Duration::zero())).count();
}
void NativeMapView::setTransitionDuration(JNIEnv&, jlong duration) {
- auto transitionOptions = map->getTransitionOptions();
+ auto transitionOptions = map->getStyle().getTransitionOptions();
transitionOptions.duration.emplace(mbgl::Milliseconds(duration));
- map->setTransitionOptions(transitionOptions);
+ map->getStyle().setTransitionOptions(transitionOptions);
}
jlong NativeMapView::getTransitionDelay(JNIEnv&) {
- const auto transitionOptions = map->getTransitionOptions();
+ const auto transitionOptions = map->getStyle().getTransitionOptions();
return std::chrono::duration_cast<std::chrono::milliseconds>(transitionOptions.delay.value_or(mbgl::Duration::zero())).count();
}
void NativeMapView::setTransitionDelay(JNIEnv&, jlong delay) {
- auto transitionOptions = map->getTransitionOptions();
+ auto transitionOptions = map->getStyle().getTransitionOptions();
transitionOptions.delay.emplace(mbgl::Milliseconds(delay));
- map->setTransitionOptions(transitionOptions);
+ map->getStyle().setTransitionOptions(transitionOptions);
}
jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object<RectF> rect) {
@@ -770,7 +618,7 @@ 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());
@@ -792,7 +640,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,
@@ -810,11 +660,13 @@ 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) {
- mbgl::style::Light* light = map->getLight();
+ mbgl::style::Light* light = map->getStyle().getLight();
if (light) {
return jni::Object<Light>(Light::createJavaLightPeer(env, *map, *light));
} else {
@@ -825,7 +677,7 @@ jni::Object<Light> NativeMapView::getLight(JNIEnv& env) {
jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) {
// Get the core layers
- std::vector<style::Layer*> layers = map->getLayers();
+ std::vector<style::Layer*> layers = map->getStyle().getLayers();
// Convert
jni::Array<jni::Object<Layer>> jLayers = jni::Array<jni::Object<Layer>>::New(env, layers.size(), Layer::javaClass);
@@ -843,7 +695,7 @@ jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) {
jni::Object<Layer> NativeMapView::getLayer(JNIEnv& env, jni::String layerId) {
// Find the layer
- mbgl::style::Layer* coreLayer = map->getLayer(jni::Make<std::string>(env, layerId));
+ mbgl::style::Layer* coreLayer = map->getStyle().getLayer(jni::Make<std::string>(env, layerId));
if (!coreLayer) {
mbgl::Log::Debug(mbgl::Event::JNI, "No layer found");
return jni::Object<Layer>();
@@ -870,7 +722,7 @@ void NativeMapView::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, jni::String
Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr);
// Find the sibling
- auto layers = map->getLayers();
+ auto layers = map->getStyle().getLayers();
auto siblingId = jni::Make<std::string>(env, above);
size_t index = 0;
@@ -905,7 +757,7 @@ void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint inde
assert(nativeLayerPtr != 0);
Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr);
- auto layers = map->getLayers();
+ auto layers = map->getStyle().getLayers();
// Check index
int numLayers = layers.size() - 1;
@@ -928,7 +780,7 @@ void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint inde
* Remove by layer id.
*/
jni::Object<Layer> NativeMapView::removeLayerById(JNIEnv& env, jni::String id) {
- std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(jni::Make<std::string>(env, id));
+ std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(jni::Make<std::string>(env, id));
if (coreLayer) {
return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer)));
} else {
@@ -940,7 +792,7 @@ jni::Object<Layer> NativeMapView::removeLayerById(JNIEnv& env, jni::String id) {
* Remove layer at index.
*/
jni::Object<Layer> NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) {
- auto layers = map->getLayers();
+ auto layers = map->getStyle().getLayers();
// Check index
int numLayers = layers.size() - 1;
@@ -949,7 +801,7 @@ jni::Object<Layer> NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) {
return jni::Object<Layer>();
}
- std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layers.at(index)->getID());
+ std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(layers.at(index)->getID());
if (coreLayer) {
return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer)));
} else {
@@ -964,7 +816,7 @@ void NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) {
assert(layerPtr != 0);
mbgl::android::Layer *layer = reinterpret_cast<mbgl::android::Layer *>(layerPtr);
- std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layer->get().getID());
+ std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(layer->get().getID());
if (coreLayer) {
layer->setLayer(std::move(coreLayer));
}
@@ -972,13 +824,13 @@ void NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) {
jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) {
// Get the core sources
- std::vector<style::Source*> sources = map->getSources();
+ std::vector<style::Source*> sources = map->getStyle().getSources();
// Convert
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));
+ auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *source));
jSources.Set(env, index, jSource);
jni::DeleteLocalRef(env, jSource);
index++;
@@ -989,14 +841,14 @@ jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) {
jni::Object<Source> NativeMapView::getSource(JNIEnv& env, jni::String sourceId) {
// Find the source
- mbgl::style::Source* coreSource = map->getSource(jni::Make<std::string>(env, sourceId));
+ mbgl::style::Source* coreSource = map->getStyle().getSource(jni::Make<std::string>(env, sourceId));
if (!coreSource) {
mbgl::Log::Debug(mbgl::Event::JNI, "No source found");
return jni::Object<Source>();
}
// Create and return the source's native peer
- return jni::Object<Source>(createJavaSourcePeer(env, *map, *coreSource));
+ return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource));
}
void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) {
@@ -1005,15 +857,16 @@ void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) {
Source *source = reinterpret_cast<Source *>(sourcePtr);
try {
source->addToMap(*map);
+ source->setRendererFrontend(*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->removeSource(jni::Make<std::string>(env, 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));
+ return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource));
} else {
return jni::Object<Source>();
}
@@ -1023,7 +876,7 @@ void NativeMapView::removeSource(JNIEnv&, jlong sourcePtr) {
assert(sourcePtr != 0);
mbgl::android::Source *source = reinterpret_cast<mbgl::android::Source *>(sourcePtr);
- std::unique_ptr<mbgl::style::Source> coreSource = map->removeSource(source->get().getID());
+ std::unique_ptr<mbgl::style::Source> coreSource = map->getStyle().removeSource(source->get().getID());
if (coreSource) {
source->setSource(std::move(coreSource));
}
@@ -1040,415 +893,31 @@ void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::ji
jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
- map->addImage(jni::Make<std::string>(env, name),
- std::make_unique<mbgl::style::Image>(std::move(premultipliedImage), float(scale)));
+ map->getStyle().addImage(std::make_unique<mbgl::style::Image>(
+ jni::Make<std::string>(env, name),
+ std::move(premultipliedImage),
+ float(scale)));
}
void NativeMapView::removeImage(JNIEnv& env, jni::String name) {
- map->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());
-
- 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;
+ map->getStyle().removeImage(jni::Make<std::string>(env, name));
}
-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");
- }
- }
- }
-
- 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::_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);
- assumeViewportSize(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 //
@@ -1463,19 +932,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"),
@@ -1508,7 +968,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"),
@@ -1528,6 +987,7 @@ 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"),
@@ -1552,9 +1012,12 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::removeSource, "nativeRemoveSource"),
METHOD(&NativeMapView::addImage, "nativeAddImage"),
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 c638de44fb..72c7b1a9eb 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -1,17 +1,14 @@
#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"
@@ -25,6 +22,7 @@
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
#include "style/light.hpp"
+#include "bitmap.hpp"
#include <exception>
#include <string>
@@ -36,7 +34,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 +50,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 +74,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);
@@ -171,8 +142,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 +180,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&);
@@ -263,72 +234,28 @@ public:
void removeImage(JNIEnv&, jni::String);
-protected:
- // mbgl::Backend //
+ jni::Object<Bitmap> getImage(JNIEnv&, jni::String);
- gl::ProcAddress initializeExtension(const char*) override;
- void activate() override;
- void deactivate() override;
+ void setPrefetchesTiles(JNIEnv&, jni::jboolean);
-private:
- void _initializeDisplay();
-
- void _terminateDisplay();
-
- void _initializeContext();
-
- void _terminateContext();
-
- void _createSurface(ANativeWindow*);
-
- void _destroySurface();
-
- EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
-
- mbgl::Size getFramebufferSize() const;
-
- 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;
diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp
index 49d28f2ebb..dff7d1d984 100644
--- a/platform/android/src/run_loop.cpp
+++ b/platform/android/src/run_loop.cpp
@@ -1,10 +1,10 @@
#include "run_loop_impl.hpp"
#include <mbgl/util/platform.hpp>
-#include <mbgl/util/thread.hpp>
-#include <mbgl/util/thread_context.hpp>
#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>
@@ -24,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];
@@ -62,9 +61,13 @@ namespace util {
// timeout, but on the main thread `ALooper_pollAll` is called by the activity
// automatically, thus we cannot set the timeout. Instead we wake the loop
// with an external file descriptor event coming from this thread.
+//
+// Usually an actor should not carry pointers to other threads, but in
+// this case the RunLoop itself owns the Alarm and calling wake() is the most
+// efficient way of waking up the RunLoop and it is also thread-safe.
class Alarm {
public:
- Alarm(RunLoop::Impl* loop_) : loop(loop_) {}
+ Alarm(ActorRef<Alarm>, RunLoop::Impl* loop_) : loop(loop_) {}
void set(const Milliseconds& timeout) {
alarm.start(timeout, mbgl::Duration::zero(), [this]() { loop->wake(); });
@@ -102,7 +105,7 @@ RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) {
case Type::Default:
ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
ALOOPER_EVENT_INPUT, looperCallbackDefault, this);
- alarm = std::make_unique<Thread<Alarm>>(ThreadContext{"Alarm"}, this);
+ alarm = std::make_unique<Thread<Alarm>>("Alarm", this);
running = true;
break;
}
@@ -190,26 +193,27 @@ Milliseconds RunLoop::Impl::processRunnables() {
auto timeout = std::chrono::duration_cast<Milliseconds>(nextDue - now);
if (alarm) {
- alarm->invoke(&Alarm::set, timeout);
+ alarm->actor().invoke(&Alarm::set, timeout);
}
return timeout;
}
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) {
diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp
new file mode 100644
index 0000000000..d64218d11a
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshotter.cpp
@@ -0,0 +1,110 @@
+#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 "../bitmap.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::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;
+ }
+
+ 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);
+ }
+
+ // 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&) {
+ MBGL_VERIFY_THREAD(tid);
+
+ snapshotCallback = std::make_unique<Actor<mbgl::MapSnapshotter::Callback>>(*Scheduler::GetCurrent(), [this](std::exception_ptr err, PremultipliedImage image) {
+ 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 bitmap
+ auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image));
+
+ // invoke callback
+ static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
+ javaPeer->Call(*_env, onSnapshotReady, bitmap);
+ }
+ });
+
+ snapshotter->snapshot(snapshotCallback->self());
+}
+
+void MapSnapshotter::cancel(JNIEnv&) {
+ MBGL_VERIFY_THREAD(tid);
+
+ snapshotCallback.reset();
+ snapshotter.reset();
+}
+
+// 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::String>,
+ "nativeInitialize",
+ "finalize",
+ 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..093f589c05
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshotter.hpp
@@ -0,0 +1,61 @@
+#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::String programCacheDir);
+
+ ~MapSnapshotter();
+
+ void start(JNIEnv&);
+
+ void cancel(JNIEnv&);
+
+private:
+ MBGL_STORE_THREAD(tid);
+
+ JavaVM *vm = nullptr;
+ GenericUniqueWeakObject<MapSnapshotter> javaPeer;
+
+ float pixelRatio;
+
+ std::shared_ptr<mbgl::ThreadPool> threadPool;
+ std::unique_ptr<Actor<mbgl::MapSnapshotter::Callback>> snapshotCallback;
+ std::unique_ptr<mbgl::MapSnapshotter> snapshotter;
+};
+
+} // 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 e2b2685928..082fe411e2 100644
--- a/platform/android/src/style/android_conversion.hpp
+++ b/platform/android/src/style/android_conversion.hpp
@@ -2,6 +2,7 @@
#include "value.hpp"
+#include <mbgl/util/feature.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/util/optional.hpp>
@@ -66,6 +67,14 @@ inline optional<float> toNumber(const mbgl::android::Value& value) {
}
}
+inline optional<double> toDouble(const mbgl::android::Value& value) {
+ if (value.isNumber()) {
+ return value.toDouble();
+ } else {
+ return {};
+ }
+}
+
inline optional<std::string> toString(const mbgl::android::Value& value) {
if (value.isString()) {
return value.toString();
diff --git a/platform/android/src/style/conversion/latlngquad.hpp b/platform/android/src/style/conversion/latlngquad.hpp
new file mode 100644
index 0000000000..9d1a83e164
--- /dev/null
+++ b/platform/android/src/style/conversion/latlngquad.hpp
@@ -0,0 +1,24 @@
+#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<std::array<LatLng, 4>> Converter<std::array<LatLng, 4>>::operator()(const mbgl::android::Value& value, Error& error) const {
+ if (value.isNull() || !value.isArray()) {
+ error = { "value cannot be converted to LatLng array" };
+ return {};
+ }
+
+ return convert<GeoJSON>(value.toString(), error);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/platform/android/src/style/conversion/transition_options.hpp b/platform/android/src/style/conversion/transition_options.hpp
index 3614878f43..ae65a32194 100644
--- a/platform/android/src/style/conversion/transition_options.hpp
+++ b/platform/android/src/style/conversion/transition_options.hpp
@@ -3,6 +3,7 @@
#include "../../conversion/conversion.hpp"
#include <jni/jni.hpp>
+#include <mbgl/style/transition_options.hpp>
#include "../../jni/local_object.hpp"
#include "../transition_options.hpp"
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/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 9bdc308d85..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))
) {
@@ -21,6 +22,10 @@ namespace android {
: Layer(map, coreLayer) {
}
+ CustomLayer::CustomLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::CustomLayer> coreLayer)
+ : Layer(map, std::move(coreLayer)) {
+ }
+
CustomLayer::~CustomLayer() = default;
void CustomLayer::update(jni::JNIEnv&) {
@@ -48,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 1173d21bfd..9e079c1288 100644
--- a/platform/android/src/style/layers/custom_layer.hpp
+++ b/platform/android/src/style/layers/custom_layer.hpp
@@ -16,10 +16,12 @@ 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&);
+ CustomLayer(mbgl::Map&, std::unique_ptr<mbgl::style::CustomLayer>);
+
~CustomLayer();
void update(jni::JNIEnv&);
diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp
index d571c3fd2e..02a1f0be82 100644
--- a/platform/android/src/style/layers/layer.cpp
+++ b/platform/android/src/style/layers/layer.cpp
@@ -3,6 +3,7 @@
#include <jni/jni.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/util/logging.hpp>
@@ -53,7 +54,7 @@ namespace android {
}
// Add layer to map
- _map.addLayer(releaseCoreLayer(), before);
+ _map.getStyle().addLayer(releaseCoreLayer(), before);
// Save pointer to the map
this->map = &_map;
@@ -91,19 +92,31 @@ namespace android {
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, mbgl::optional<std::string>());
+ optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value);
if (error) {
mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message);
return;
}
}
+ struct SetFilterEvaluator {
+ style::Filter filter;
+
+ void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support filters"); }
+ void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support filters"); }
+ void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support filters"); }
+
+ template <class LayerType>
+ void operator()(LayerType& layer) {
+ layer.setFilter(filter);
+ }
+ };
+
void Layer::setFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) {
using namespace mbgl::style;
using namespace mbgl::style::conversion;
Value wrapped(env, jfilter);
- Filter filter;
Error error;
optional<Filter> converted = convert<Filter>(wrapped, error);
@@ -111,62 +124,45 @@ namespace android {
mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + error.message);
return;
}
- filter = std::move(*converted);
-
- if (layer.is<FillLayer>()) {
- layer.as<FillLayer>()->setFilter(filter);
- } else if (layer.is<LineLayer>()) {
- layer.as<LineLayer>()->setFilter(filter);
- } else if (layer.is<SymbolLayer>()) {
- layer.as<SymbolLayer>()->setFilter(filter);
- } else if (layer.is<CircleLayer>()) {
- layer.as<CircleLayer>()->setFilter(filter);
- } else if (layer.is<FillExtrusionLayer>()){
- layer.as<FillExtrusionLayer>()->setFilter(filter);
- } else {
- mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support filters");
- }
+
+ layer.accept(SetFilterEvaluator {std::move(*converted)});
}
- void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) {
- using namespace mbgl::style;
+ struct SetSourceLayerEvaluator {
+ std::string sourceLayer;
- std::string layerId = jni::Make<std::string>(env, sourceLayer);
-
- if (layer.is<FillLayer>()) {
- layer.as<FillLayer>()->setSourceLayer(layerId);
- } else if (layer.is<LineLayer>()) {
- layer.as<LineLayer>()->setSourceLayer(layerId);
- } else if (layer.is<SymbolLayer>()) {
- layer.as<SymbolLayer>()->setSourceLayer(layerId);
- } else if (layer.is<CircleLayer>()) {
- layer.as<CircleLayer>()->setSourceLayer(layerId);
- } else if(layer.is<FillExtrusionLayer>()) {
- layer.as<FillExtrusionLayer>()->setSourceLayer(layerId);
- } else {
- mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer");
+ void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support source layer"); }
+ void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support source layer"); }
+ void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support source layer"); }
+
+ template <class LayerType>
+ void operator()(LayerType& layer) {
+ layer.setSourceLayer(sourceLayer);
}
+ };
+
+ void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) {
+ layer.accept(SetSourceLayerEvaluator {jni::Make<std::string>(env, sourceLayer)});
}
- jni::String Layer::getSourceLayer(jni::JNIEnv& env) {
- using namespace mbgl::style;
+ struct GetSourceLayerEvaluator {
+ std::string noop(std::string layerType) {
+ Log::Warning(mbgl::Event::JNI, "%s doesn't support source layer", layerType.c_str());
+ return {};
+ }
- std::string sourceLayerId;
- if (layer.is<FillLayer>()) {
- sourceLayerId = layer.as<FillLayer>()->getSourceLayer();
- } else if (layer.is<LineLayer>()) {
- sourceLayerId = layer.as<LineLayer>()->getSourceLayer();
- } else if (layer.is<SymbolLayer>()) {
- sourceLayerId = layer.as<SymbolLayer>()->getSourceLayer();
- } else if (layer.is<CircleLayer>()) {
- sourceLayerId = layer.as<CircleLayer>()->getSourceLayer();
- } else if (layer.is<FillExtrusionLayer>()) {
- sourceLayerId = layer.as<FillExtrusionLayer>()->getSourceLayer();
- } else {
- mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer");
+ std::string operator()(style::BackgroundLayer&) { return noop("BackgroundLayer"); }
+ std::string operator()(style::CustomLayer&) { return noop("CustomLayer"); }
+ std::string operator()(style::RasterLayer&) { return noop("RasterLayer"); }
+
+ template <class LayerType>
+ std::string operator()(LayerType& layer) {
+ return layer.getSourceLayer();
}
+ };
- return jni::Make<jni::String>(env, sourceLayerId);
+ jni::String Layer::getSourceLayer(jni::JNIEnv& env) {
+ return jni::Make<jni::String>(env, layer.accept(GetSourceLayerEvaluator()));
}
jni::jfloat Layer::getMinZoom(jni::JNIEnv&){
diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp
index 5c49f875ee..9803b6f841 100644
--- a/platform/android/src/style/layers/layers.cpp
+++ b/platform/android/src/style/layers/layers.cpp
@@ -24,53 +24,51 @@
namespace mbgl {
namespace android {
-static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) {
- if (coreLayer.is<mbgl::style::BackgroundLayer>()) {
- return new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>());
- } else if (coreLayer.is<mbgl::style::CircleLayer>()) {
- return new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>());
- } else if (coreLayer.is<mbgl::style::FillExtrusionLayer>()) {
- return new FillExtrusionLayer(map, *coreLayer.as<mbgl::style::FillExtrusionLayer>());
- } else if (coreLayer.is<mbgl::style::FillLayer>()) {
- return new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>());
- } else if (coreLayer.is<mbgl::style::LineLayer>()) {
- return new LineLayer(map, *coreLayer.as<mbgl::style::LineLayer>());
- } else if (coreLayer.is<mbgl::style::RasterLayer>()) {
- return new RasterLayer(map, *coreLayer.as<mbgl::style::RasterLayer>());
- } else if (coreLayer.is<mbgl::style::SymbolLayer>()) {
- return new SymbolLayer(map, *coreLayer.as<mbgl::style::SymbolLayer>());
- } else if (coreLayer.is<mbgl::style::CustomLayer>()) {
- return new CustomLayer(map, *coreLayer.as<mbgl::style::CustomLayer>());
- } else {
- return new UnknownLayer(map, coreLayer);
+// Mapping from style layers to peer classes
+template <class> struct PeerType {};
+template <> struct PeerType<style::BackgroundLayer> { using Type = android::BackgroundLayer; };
+template <> struct PeerType<style::CircleLayer> { using Type = android::CircleLayer; };
+template <> struct PeerType<style::FillExtrusionLayer> { using Type = android::FillExtrusionLayer; };
+template <> struct PeerType<style::FillLayer> { using Type = android::FillLayer; };
+template <> struct PeerType<style::LineLayer> { using Type = android::LineLayer; };
+template <> struct PeerType<style::RasterLayer> { using Type = android::RasterLayer; };
+template <> struct PeerType<style::SymbolLayer> { using Type = android::SymbolLayer; };
+template <> struct PeerType<style::CustomLayer> { using Type = android::CustomLayer; };
+
+// Inititalizes a non-owning peer
+struct LayerPeerIntitializer {
+ mbgl::Map& map;
+
+ template <class LayerType>
+ Layer* operator()(LayerType& layer) {
+ return new typename PeerType<LayerType>::Type(map, layer);
}
-}
+};
-template <class LayerType, class PeerType>
-static PeerType* createPeer(Map& map, std::unique_ptr<mbgl::style::Layer> layer) {
- return new PeerType(map, std::move(std::unique_ptr<LayerType>(layer.release()->as<LayerType>())));
+static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) {
+ Layer* layer = coreLayer.accept(LayerPeerIntitializer {map});
+ return layer ? layer : new UnknownLayer(map, coreLayer);
}
-static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) {
- if (coreLayer->is<style::BackgroundLayer>()) {
- return createPeer<style::BackgroundLayer, BackgroundLayer>(map, std::move(coreLayer));
- } else if (coreLayer->is<style::CircleLayer>()) {
- return createPeer<style::CircleLayer, CircleLayer>(map, std::move(coreLayer));
- } else if (coreLayer->is<style::FillExtrusionLayer>()) {
- return createPeer<style::FillExtrusionLayer, FillExtrusionLayer>(map, std::move(coreLayer));
- } else if (coreLayer->is<style::FillLayer>()) {
- return createPeer<style::FillLayer, FillLayer>(map, std::move(coreLayer));
- } else if (coreLayer->is<style::LineLayer>()) {
- return createPeer<style::LineLayer, LineLayer>(map, std::move(coreLayer));
- } else if (coreLayer->is<style::RasterLayer>()) {
- return createPeer<style::RasterLayer, RasterLayer>(map, std::move(coreLayer));
- } else if (coreLayer->is<style::SymbolLayer>()) {
- return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer));
- } else if (coreLayer->is<mbgl::style::CustomLayer>()) {
- return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer));
- } else {
- return new UnknownLayer(map, std::move(coreLayer));
+// Initializes an owning peer
+// Only usable once since it needs to pass on ownership
+// of the given layer and thus enforced to be an rvalue
+struct UniqueLayerPeerIntitializer {
+ mbgl::Map& map;
+ std::unique_ptr<style::Layer> layer;
+
+ template <class LayerType>
+ Layer* operator()(LayerType&) && {
+ return new typename PeerType<LayerType>::Type(
+ map,
+ std::unique_ptr<LayerType>(layer.release()->as<LayerType>())
+ );
}
+};
+
+static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) {
+ Layer* layer = coreLayer->accept(UniqueLayerPeerIntitializer {map, std::move(coreLayer)});
+ return layer ? layer : new UnknownLayer(map, std::move(coreLayer));
}
jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, Map& map, style::Layer& coreLayer) {
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/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp
index 780cc4b6f6..90ef851eba 100644
--- a/platform/android/src/style/sources/geojson_source.cpp
+++ b/platform/android/src/style/sources/geojson_source.cpp
@@ -1,5 +1,7 @@
#include "geojson_source.hpp"
+#include <mbgl/renderer/query.hpp>
+
// Java -> C++ conversion
#include "../android_conversion.hpp"
#include "../conversion/filter.hpp"
@@ -41,8 +43,8 @@ namespace android {
) {
}
- GeoJSONSource::GeoJSONSource(mbgl::Map& map, mbgl::style::GeoJSONSource& coreSource)
- : Source(map, coreSource) {
+ GeoJSONSource::GeoJSONSource(mbgl::style::GeoJSONSource& coreSource)
+ : Source(coreSource) {
}
GeoJSONSource::~GeoJSONSource() = default;
@@ -108,8 +110,8 @@ 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);
}
diff --git a/platform/android/src/style/sources/geojson_source.hpp b/platform/android/src/style/sources/geojson_source.hpp
index 938a20612c..52dd632bfa 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(mbgl::style::GeoJSONSource&);
~GeoJSONSource();
diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp
new file mode 100644
index 0000000000..d46b367c53
--- /dev/null
+++ b/platform/android/src/style/sources/image_source.cpp
@@ -0,0 +1,72 @@
+#include "image_source.hpp"
+
+// Java -> C++ conversion
+#include "../android_conversion.hpp"
+
+// C++ -> Java conversion
+#include "../../conversion/conversion.hpp"
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/premultiply.hpp>
+
+#include "../../bitmap.hpp"
+#include <string>
+#include <array>
+
+namespace mbgl {
+namespace android {
+
+ ImageSource::ImageSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<LatLngQuad> coordinatesObject)
+ : Source(env, std::make_unique<mbgl::style::ImageSource>(
+ jni::Make<std::string>(env, sourceId),
+ LatLngQuad::getLatLngArray(env, coordinatesObject)
+ )
+ ) {
+ }
+
+ ImageSource::ImageSource(mbgl::style::ImageSource& coreSource)
+ : Source(coreSource) {
+ }
+
+ ImageSource::~ImageSource() = default;
+
+ void ImageSource::setURL(jni::JNIEnv& env, jni::String url) {
+ // Update the core source
+ source.as<mbgl::style::ImageSource>()->ImageSource::setURL(jni::Make<std::string>(env, url));
+ }
+
+ jni::String ImageSource::getURL(jni::JNIEnv& env) {
+ optional<std::string> url = source.as<mbgl::style::ImageSource>()->ImageSource::getURL();
+ return url ? jni::Make<jni::String>(env, *url) : jni::String();
+ }
+
+ void ImageSource::setImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap) {
+ source.as<mbgl::style::ImageSource>()->setImage(Bitmap::GetImage(env, bitmap));
+ }
+
+ jni::Class<ImageSource> ImageSource::javaClass;
+
+ jni::jobject* 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));
+ }
+
+ void ImageSource::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ ImageSource::javaClass = *jni::Class<ImageSource>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<ImageSource>(
+ env, ImageSource::javaClass, "nativePtr",
+ std::make_unique<ImageSource, JNIEnv&, jni::String, jni::Object<LatLngQuad>>,
+ "initialize",
+ "finalize",
+ METHOD(&ImageSource::setURL, "nativeSetUrl"),
+ METHOD(&ImageSource::getURL, "nativeGetUrl"),
+ METHOD(&ImageSource::setImage, "nativeSetImage")
+ );
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/image_source.hpp b/platform/android/src/style/sources/image_source.hpp
new file mode 100644
index 0000000000..9787a7294f
--- /dev/null
+++ b/platform/android/src/style/sources/image_source.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "source.hpp"
+#include "../../geometry/lat_lng_quad.hpp"
+#include <mbgl/style/sources/image_source.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class Bitmap;
+
+class ImageSource : public Source {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/ImageSource"; };
+
+ static jni::Class<ImageSource> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ ImageSource(jni::JNIEnv&, jni::String, jni::Object<LatLngQuad>);
+
+ ImageSource(mbgl::style::ImageSource&);
+
+ ~ImageSource();
+
+ void setURL(jni::JNIEnv&, jni::String);
+ jni::String getURL(jni::JNIEnv&);
+
+ void setImage(jni::JNIEnv&, jni::Object<Bitmap>);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class ImageSource
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp
index 32fdb163b0..d45342a1ad 100644
--- a/platform/android/src/style/sources/raster_source.cpp
+++ b/platform/android/src/style/sources/raster_source.cpp
@@ -22,8 +22,8 @@ namespace android {
) {
}
- RasterSource::RasterSource(mbgl::Map& map, mbgl::style::RasterSource& coreSource)
- : Source(map, coreSource) {
+ RasterSource::RasterSource(mbgl::style::RasterSource& coreSource)
+ : Source(coreSource) {
}
RasterSource::~RasterSource() = default;
diff --git a/platform/android/src/style/sources/raster_source.hpp b/platform/android/src/style/sources/raster_source.hpp
index a79ccc10a4..84c49d7381 100644
--- a/platform/android/src/style/sources/raster_source.hpp
+++ b/platform/android/src/style/sources/raster_source.hpp
@@ -18,7 +18,7 @@ public:
RasterSource(jni::JNIEnv&, jni::String, jni::Object<>, jni::jint);
- RasterSource(mbgl::Map&, mbgl::style::RasterSource&);
+ RasterSource(mbgl::style::RasterSource&);
~RasterSource();
diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp
index e0e9bb9870..447b13019d 100644
--- a/platform/android/src/style/sources/source.cpp
+++ b/platform/android/src/style/sources/source.cpp
@@ -3,6 +3,7 @@
#include <jni/jni.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/util/logging.hpp>
// Java -> C++ conversion
@@ -25,11 +26,11 @@ namespace android {
, source(*ownedSource) {
}
- Source::Source(mbgl::Map& coreMap, mbgl::style::Source& coreSource) : source(coreSource) , map(&coreMap) {
+ Source::Source(mbgl::style::Source& coreSource)
+ : source(coreSource) {
}
- Source::~Source() {
- }
+ Source::~Source() = default;
style::Source& Source::get() {
return source;
@@ -55,10 +56,11 @@ namespace android {
}
// Add source to map
- _map.addSource(releaseCoreSource());
+ _map.getStyle().addSource(releaseCoreSource());
+ }
- // Save pointer to the map
- this->map = &_map;
+ void Source::setRendererFrontend(AndroidRendererFrontend& frontend_) {
+ rendererFrontend = &frontend_;
}
std::unique_ptr<mbgl::style::Source> Source::releaseCoreSource() {
diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp
index 49fc50d754..383017b66f 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>
@@ -23,7 +24,7 @@ public:
/*
* Called when a Java object is created on the c++ side
*/
- Source(mbgl::Map&, mbgl::style::Source&);
+ Source(mbgl::style::Source&);
/*
* Called when a Java object was created from the jvm side
@@ -41,6 +42,8 @@ public:
void addToMap(mbgl::Map&);
+ void setRendererFrontend(AndroidRendererFrontend&);
+
virtual jni::jobject* createJavaPeer(jni::JNIEnv&) = 0;
jni::String getId(jni::JNIEnv&);
@@ -57,8 +60,9 @@ protected:
// Raw pointer that is valid until the source is removed from the map
mbgl::style::Source& source;
- // Map pointer is valid for newly created sources only after adding to the map
- mbgl::Map* map;
+ // RendererFrontend pointer is valid only when
+ // added to the map
+ AndroidRendererFrontend* rendererFrontend;
};
} // namespace android
diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp
index b4e70202b4..9ab3ca8e84 100644
--- a/platform/android/src/style/sources/sources.cpp
+++ b/platform/android/src/style/sources/sources.cpp
@@ -2,35 +2,46 @@
#include <mbgl/style/source.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/image_source.hpp>
#include <mbgl/style/sources/raster_source.hpp>
#include <mbgl/style/sources/vector_source.hpp>
#include "source.hpp"
#include "geojson_source.hpp"
+#include "image_source.hpp"
#include "raster_source.hpp"
#include "unknown_source.hpp"
#include "vector_source.hpp"
+namespace {
+
+ using namespace mbgl::android;
+
+ Source* initializeSourcePeer(mbgl::style::Source& coreSource) {
+ Source* source;
+ if (coreSource.is<mbgl::style::VectorSource>()) {
+ source = new VectorSource(*coreSource.as<mbgl::style::VectorSource>());
+ } else if (coreSource.is<mbgl::style::RasterSource>()) {
+ source = new RasterSource(*coreSource.as<mbgl::style::RasterSource>());
+ } else if (coreSource.is<mbgl::style::GeoJSONSource>()) {
+ source = new GeoJSONSource(*coreSource.as<mbgl::style::GeoJSONSource>());
+ } else if (coreSource.is<mbgl::style::ImageSource>()) {
+ source = new ImageSource(*coreSource.as<mbgl::style::ImageSource>());
+ } else {
+ source = new UnknownSource(coreSource);
+ }
+
+ return source;
+ }
+} // namespace
+
namespace mbgl {
namespace android {
-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 {
- 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* createJavaSourcePeer(jni::JNIEnv& env, AndroidRendererFrontend& frontend, mbgl::style::Source& coreSource) {
+ std::unique_ptr<Source> peerSource = std::unique_ptr<Source>(initializeSourcePeer(coreSource));
+ peerSource->setRendererFrontend(frontend);
jni::jobject* result = peerSource->createJavaPeer(env);
peerSource.release();
return result;
@@ -39,6 +50,7 @@ jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style
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
index 09a8b35067..c7b36818b2 100644
--- a/platform/android/src/style/sources/sources.hpp
+++ b/platform/android/src/style/sources/sources.hpp
@@ -1,20 +1,18 @@
#pragma once
-#include <mbgl/map/map.hpp>
#include <mbgl/style/source.hpp>
#include "source.hpp"
+#include "../../android_renderer_frontend.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&);
+ jni::jobject* createJavaSourcePeer(jni::JNIEnv&, AndroidRendererFrontend&, mbgl::style::Source&);
void registerNativeSources(jni::JNIEnv&);
-}
-}
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/unknown_source.cpp b/platform/android/src/style/sources/unknown_source.cpp
index 86736597c3..79f27bdfbf 100644
--- a/platform/android/src/style/sources/unknown_source.cpp
+++ b/platform/android/src/style/sources/unknown_source.cpp
@@ -12,8 +12,8 @@ namespace {
namespace mbgl {
namespace android {
- UnknownSource::UnknownSource(mbgl::Map& map, mbgl::style::Source& coreSource)
- : Source(map, coreSource) {
+ UnknownSource::UnknownSource(mbgl::style::Source& coreSource)
+ : Source(coreSource) {
}
jni::Class<UnknownSource> UnknownSource::javaClass;
diff --git a/platform/android/src/style/sources/unknown_source.hpp b/platform/android/src/style/sources/unknown_source.hpp
index 3c37239792..4a003c9a7f 100644
--- a/platform/android/src/style/sources/unknown_source.hpp
+++ b/platform/android/src/style/sources/unknown_source.hpp
@@ -16,7 +16,7 @@ public:
static void registerNative(jni::JNIEnv&);
- UnknownSource(mbgl::Map&, mbgl::style::Source&);
+ UnknownSource(mbgl::style::Source&);
~UnknownSource() = default;
diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp
index e2d9f60dec..7fe45441bd 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,8 @@ namespace android {
) {
}
- VectorSource::VectorSource(mbgl::Map& map, mbgl::style::VectorSource& coreSource)
- : Source(map, coreSource) {
+ VectorSource::VectorSource(mbgl::style::VectorSource& coreSource)
+ : Source(coreSource) {
}
VectorSource::~VectorSource() = default;
@@ -46,8 +48,8 @@ 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);
}
diff --git a/platform/android/src/style/sources/vector_source.hpp b/platform/android/src/style/sources/vector_source.hpp
index 643b468338..509fe068d1 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(mbgl::style::VectorSource&);
~VectorSource();
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 26c533632f..06a8907704 100644
--- a/platform/darwin/docs/guides/For Style Authors.md.ejs
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -160,7 +160,6 @@ the following terms for concepts defined in the style specification:
In the style specification | In the SDK
---------------------------|---------
-class | style class
filter | predicate
function type | interpolation mode
id | identifier
@@ -181,8 +180,9 @@ In style JSON | In the SDK
`geojson` | `MGLShapeSource`
`raster` | `MGLRasterSource`
`vector` | `MGLVectorSource`
+`image` | `MGLImageSource`
-`canvas`, `image`, and `video` sources are not supported.
+`canvas` and `video` sources are not supported.
### Tile sources
@@ -223,6 +223,12 @@ To create a shape source from local GeoJSON data, first
[convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects),
then use the `-[MGLShapeSource initWithIdentifier:shape:options:]` method.
+### Image sources
+
+Image sources accept a non-axis aligned quadrilateral as their geographic coordinates.
+These coordinates, in `MGLCoordinateQuad`, are described in counterclockwise order,
+in contrast to the clockwise order defined in the style specification.
+
## Configuring the map content’s appearance
Each layer defined by the style JSON file is represented at runtime by a style
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 5a4936c0ee..4ee86e65da 100644
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -148,6 +148,9 @@ 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}`;
@@ -500,6 +503,9 @@ global.mbglType = function(property) {
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':
@@ -648,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;
}
@@ -663,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/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/MGLConversion.h b/platform/darwin/src/MGLConversion.h
index d51ebd775c..d6363b28eb 100644
--- a/platform/darwin/src/MGLConversion.h
+++ b/platform/darwin/src/MGLConversion.h
@@ -104,6 +104,14 @@ inline optional<float> toNumber(const id value) {
}
}
+inline optional<double> toDouble(const id value) {
+ if (_isNumber(value)) {
+ return ((NSNumber *)value).doubleValue;
+ } else {
+ return {};
+ }
+}
+
inline optional<std::string> toString(const id value) {
if (_isString(value)) {
return std::string(static_cast<const char *>([value UTF8String]));
diff --git a/platform/darwin/src/MGLGeometry.h b/platform/darwin/src/MGLGeometry.h
index d37741cde5..7c68033abf 100644
--- a/platform/darwin/src/MGLGeometry.h
+++ b/platform/darwin/src/MGLGeometry.h
@@ -45,6 +45,24 @@ typedef struct __attribute__((objc_boxable)) MGLCoordinateBounds {
CLLocationCoordinate2D ne;
} MGLCoordinateBounds;
+/**
+ A quadrilateral area as measured on a two-dimensional map projection.
+ `MGLCoordinateQuad` differs from `MGLCoordinateBounds` in that it allows
+ representation of non-axis aligned bounds and non-rectangular quadrilaterals.
+ The coordinates are described in counter clockwise order from top left.
+ */
+typedef struct MGLCoordinateQuad {
+ /** Coordinate at the top left corner. */
+ CLLocationCoordinate2D topLeft;
+ /** Coordinate at the bottom left corner. */
+ CLLocationCoordinate2D bottomLeft;
+ /** Coordinate at the bottom right corner. */
+ CLLocationCoordinate2D bottomRight;
+ /** Coordinate at the top right corner. */
+ CLLocationCoordinate2D topRight;
+} MGLCoordinateQuad;
+
+
/**
Creates a new `MGLCoordinateBounds` structure from the given southwest and
northeast coordinates.
@@ -56,6 +74,33 @@ NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsMake(CLLocationCoordinate2D sw,
return bounds;
}
+/**
+ Creates a new `MGLCoordinateQuad` structure from the given top left,
+ bottom left, bottom right, and top right coordinates.
+ */
+NS_INLINE MGLCoordinateQuad MGLCoordinateQuadMake(CLLocationCoordinate2D topLeft, CLLocationCoordinate2D bottomLeft, CLLocationCoordinate2D bottomRight, CLLocationCoordinate2D topRight) {
+ MGLCoordinateQuad quad;
+ quad.topLeft = topLeft;
+ quad.bottomLeft = bottomLeft;
+ quad.bottomRight = bottomRight;
+ quad.topRight = topRight;
+ return quad;
+}
+
+/**
+ Creates a new `MGLCoordinateQuad` structure from the given `MGLCoordinateBounds`.
+ The returned quad uses the bounds' northeast coordinate as the top right, and the
+ southwest coordinate at the bottom left.
+ */
+NS_INLINE MGLCoordinateQuad MGLCoordinateQuadFromCoordinateBounds(MGLCoordinateBounds bounds) {
+ MGLCoordinateQuad quad;
+ quad.topLeft = CLLocationCoordinate2DMake(bounds.ne.latitude, bounds.sw.longitude);
+ quad.bottomLeft = bounds.sw;
+ quad.bottomRight = CLLocationCoordinate2DMake(bounds.sw.latitude, bounds.ne.longitude);
+ quad.topRight = bounds.ne;
+ return quad;
+}
+
/** Returns `YES` if the two coordinate bounds are equal to each other. */
NS_INLINE BOOL MGLCoordinateBoundsEqualToCoordinateBounds(MGLCoordinateBounds bounds1, MGLCoordinateBounds bounds2) {
return (bounds1.sw.latitude == bounds2.sw.latitude &&
@@ -117,6 +162,15 @@ NS_INLINE NSString *MGLStringFromCoordinateBounds(MGLCoordinateBounds bounds) {
bounds.ne.latitude, bounds.ne.longitude];
}
+/** Returns a formatted string for the given coordinate quad. */
+NS_INLINE NSString *MGLStringFromCoordinateQuad(MGLCoordinateQuad quad) {
+ return [NSString stringWithFormat:@"{ topleft = {%.1f, %.1f}, bottomleft = {%.1f, %.1f}}, bottomright = {%.1f, %.1f}, topright = {%.1f, %.1f}",
+ quad.topLeft.latitude, quad.topLeft.longitude,
+ quad.bottomLeft.latitude, quad.bottomLeft.longitude,
+ quad.bottomRight.latitude, quad.bottomRight.longitude,
+ quad.topRight.latitude, quad.topRight.longitude];
+}
+
/** Returns radians, converted from degrees. */
NS_INLINE CGFloat MGLRadiansFromDegrees(CLLocationDegrees degrees) {
return (CGFloat)(degrees * M_PI) / 180;
diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h
index 7ad8314a79..88fcf5b576 100644
--- a/platform/darwin/src/MGLGeometry_Private.h
+++ b/platform/darwin/src/MGLGeometry_Private.h
@@ -8,6 +8,7 @@
#import <mbgl/util/geo.hpp>
#import <mbgl/util/geometry.hpp>
+#import <array>
typedef double MGLLocationRadians;
typedef double MGLRadianDistance;
typedef double MGLRadianDirection;
@@ -56,6 +57,20 @@ NS_INLINE mbgl::LatLngBounds MGLLatLngBoundsFromCoordinateBounds(MGLCoordinateBo
MGLLatLngFromLocationCoordinate2D(coordinateBounds.ne));
}
+NS_INLINE std::array<mbgl::LatLng, 4> MGLLatLngArrayFromCoordinateQuad(MGLCoordinateQuad quad) {
+ return { MGLLatLngFromLocationCoordinate2D(quad.topLeft),
+ MGLLatLngFromLocationCoordinate2D(quad.topRight),
+ MGLLatLngFromLocationCoordinate2D(quad.bottomRight),
+ MGLLatLngFromLocationCoordinate2D(quad.bottomLeft) };
+}
+
+NS_INLINE MGLCoordinateQuad MGLCoordinateQuadFromLatLngArray(std::array<mbgl::LatLng, 4> quad) {
+ return { MGLLocationCoordinate2DFromLatLng(quad[0]),
+ MGLLocationCoordinate2DFromLatLng(quad[3]),
+ MGLLocationCoordinate2DFromLatLng(quad[2]),
+ MGLLocationCoordinate2DFromLatLng(quad[1]) };
+}
+
#if TARGET_OS_IPHONE
NS_INLINE mbgl::EdgeInsets MGLEdgeInsetsFromNSEdgeInsets(UIEdgeInsets insets) {
return { insets.top, insets.left, insets.bottom, insets.right };
diff --git a/platform/darwin/src/MGLImageSource.h b/platform/darwin/src/MGLImageSource.h
new file mode 100644
index 0000000000..21487d9739
--- /dev/null
+++ b/platform/darwin/src/MGLImageSource.h
@@ -0,0 +1,94 @@
+#import "MGLSource.h"
+
+#import "MGLFoundation.h"
+#import "MGLTypes.h"
+#import "MGLGeometry.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+MGL_EXPORT
+/**
+ `MGLImageSource` is a content source that is used for a georeferenced raster
+ image to be shown on the map. The georeferenced image scales and rotates as the
+ user zooms and rotates the map. Images may also be used as icons or patterns
+ in a style layer. To register an image for use as an icon or pattern,
+ use the `-[MGLStyle setImage:forName:]` method. To configure a point
+ annotation’s image, use the `MGLAnnotationImage` class.
+
+ The geographic location of the raster image content, supplied with
+ `MGLCoordinateQuad`, can be non-axis aligned.
+ `MGLImageSource` supports raster content from `NSURL`, `NSImage` (macOS), or
+ `UIImage` (iOS).
+ An image source is added to an `MGLStyle` object along with one or more
+ `MGLRasterStyleLayer` objects. Use a raster style layer to control the
+ appearance of content supplied by the image source.
+
+ Each
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-image"><code>image</code></a>
+ source defined by the style JSON file is represented at runtime by an
+ `MGLImageSource` object that you can use to initialize new style layers. You
+ can also add and remove sources dynamically using methods such as
+ `-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
+
+ ### Example
+
+ ```swift
+ let coordinates = MGLCoordinateQuad(
+ topLeft: CLLocationCoordinate2D(latitude: 46.437, longitude: -80.425),
+ 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")!)
+ mapView.style?.addSource(source)
+ ```
+ */
+MGL_EXPORT
+@interface MGLImageSource : MGLSource
+
+#pragma mark Initializing a Source
+
+/**
+ Returns a georeferenced image source with an identifier, coordinates and a URL.
+
+ @param identifier A string that uniquely identifies the source.
+ @param coordinateQuad the top left, top right, bottom right, and bottom left coordinates for the image.
+ @param url An HTTP(S) URL, absolute file URL, or local file URL relative to the
+ current application’s resource bundle.
+ @return An initialized shape source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MGLCoordinateQuad)coordinateQuad URL:(NSURL *)url;
+
+/**
+ Returns a georeferenced image source with an identifier, coordinates and an image.
+
+ @param identifier A string that uniquely identifies the source.
+ @param coordinateQuad The top left, top right, bottom right, and bottom left coordinates for the image.
+ @param image The image to display for the source.
+ @return An initialized shape source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MGLCoordinateQuad)coordinateQuad image:(MGLImage *)image;
+
+#pragma mark Accessing a Source’s Content
+
+/**
+ The URL to the source image.
+
+ If the receiver was initialized using `-initWithIdentifier:coordinateQuad:image:` or
+ the `image` property is set, this property is set to `nil`.
+ */
+@property (nonatomic, copy, nullable)NSURL *URL;
+
+/**
+ The source image.
+
+ If the receiver was initialized using `-initWithIdentifier:coordinateQuad:URL:` or if the `URL` property is set, this property is set to `nil`.
+ */
+@property (nonatomic, retain, nullable)MGLImage *image;
+
+/**
+ The coordinates at which the corners of the source image will be placed.
+ */
+@property (nonatomic) MGLCoordinateQuad coordinates;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLImageSource.mm b/platform/darwin/src/MGLImageSource.mm
new file mode 100644
index 0000000000..351247e901
--- /dev/null
+++ b/platform/darwin/src/MGLImageSource.mm
@@ -0,0 +1,92 @@
+#import "MGLImageSource.h"
+
+#import "MGLGeometry_Private.h"
+#import "MGLSource_Private.h"
+#import "MGLTileSource_Private.h"
+#import "NSURL+MGLAdditions.h"
+#if TARGET_OS_IPHONE
+ #import "UIImage+MGLAdditions.h"
+#else
+ #import "NSImage+MGLAdditions.h"
+#endif
+
+#include <mbgl/style/sources/image_source.hpp>
+#include <mbgl/util/premultiply.hpp>
+
+@interface MGLImageSource ()
+- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MGLCoordinateQuad)coordinateQuad NS_DESIGNATED_INITIALIZER;
+
+@property (nonatomic, readonly) mbgl::style::ImageSource *rawSource;
+
+@end
+
+@implementation MGLImageSource
+
+- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MGLCoordinateQuad)coordinateQuad {
+
+ const auto coordsArray = MGLLatLngArrayFromCoordinateQuad(coordinateQuad);
+ auto source = std::make_unique<mbgl::style::ImageSource>(identifier.UTF8String, coordsArray);
+ return self = [super initWithPendingSource:std::move(source)];
+}
+
+
+- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MGLCoordinateQuad)coordinateQuad URL:(NSURL *)url {
+ self = [self initWithIdentifier:identifier coordinateQuad: coordinateQuad];
+ self.URL = url;
+ return self;
+}
+
+
+- (instancetype)initWithIdentifier:(NSString *)identifier coordinateQuad:(MGLCoordinateQuad)coordinateQuad image:(MGLImage *)image {
+ self = [self initWithIdentifier:identifier coordinateQuad: coordinateQuad];
+ self.image = image;
+
+ return self;
+}
+
+- (NSURL *)URL {
+ auto url = self.rawSource->getURL();
+ return url ? [NSURL URLWithString:@(url->c_str())] : nil;
+}
+
+- (void)setURL:(NSURL *)url {
+ if (url) {
+ self.rawSource->setURL(url.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
+ _image = nil;
+ } else {
+ self.image = nullptr;
+ }
+}
+
+- (void)setImage:(MGLImage *)image {
+ if (image != nullptr) {
+ self.rawSource->setImage(image.mgl_premultipliedImage);
+ } else {
+ self.rawSource->setImage(mbgl::PremultipliedImage({0,0}));
+ }
+ _image = image;
+}
+
+- (MGLCoordinateQuad)coordinates {
+ return MGLCoordinateQuadFromLatLngArray(self.rawSource->getCoordinates());
+}
+
+- (void)setCoordinates: (MGLCoordinateQuad)coordinateQuad {
+ self.rawSource->setCoordinates(MGLLatLngArrayFromCoordinateQuad(coordinateQuad));
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@; coordinates = %@; URL = %@; image = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier, MGLStringFromCoordinateQuad(self.coordinates), self.URL, self.image];
+}
+
+- (mbgl::style::ImageSource *)rawSource {
+ return (mbgl::style::ImageSource *)super.rawSource;
+}
+
+- (NSString *)attributionHTMLString {
+ auto attribution = self.rawSource->getAttribution();
+ return attribution ? @(attribution->c_str()) : nil;
+}
+
+@end
diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h
index 50db3f45fd..55b789f043 100644
--- a/platform/darwin/src/MGLLight.h
+++ b/platform/darwin/src/MGLLight.h
@@ -27,7 +27,7 @@ typedef NS_ENUM(NSUInteger, MGLLightAnchor) {
A structure containing information about the position of the light source
relative to lit geometries.
*/
-typedef struct __attribute__((objc_boxable)) MGLSphericalPosition {
+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
diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h
index 38513652c5..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;
@@ -543,6 +553,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 *> *lineWidth;
diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm
index 8b90efd0c4..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 {
@@ -480,7 +480,7 @@ namespace mbgl {
- (void)setLineWidth:(MGLStyleValue<NSNumber *> *)lineWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(lineWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineWidth);
self.rawLayer->setLineWidth(mbglValue);
}
@@ -489,9 +489,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getLineWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultLineWidth());
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineWidth());
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
}
- (void)setLineWidthTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h
new file mode 100644
index 0000000000..a2a4f1b331
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -0,0 +1,105 @@
+#import <Foundation/Foundation.h>
+#import "MGLTypes.h"
+#import "MGLGeometry.h"
+#import "MGLMapCamera.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+MGL_EXPORT
+/**
+ The options to use when creating images with the `MGLMapsnapshotter`.
+ */
+@interface MGLMapSnapshotOptions : NSObject
+
+/**
+ Creates a set of options with the minimum required information
+ @param styleURL the style url to use
+ @param camera the camera settings
+ @param size the image size
+ */
+- (instancetype)initWithStyleURL:(NSURL*)styleURL camera:(MGLMapCamera*)camera size:(CGSize)size;
+
+#pragma mark - Configuring the map
+
+/**
+ The style URL for these options.
+ */
+@property (nonatomic, readonly) NSURL* styleURL;
+
+/**
+ The zoom. Default is 0.
+ */
+@property (nonatomic) double zoom;
+
+/**
+ The `MGLMapcamera` options to use.
+ */
+@property (nonatomic) MGLMapCamera* camera;
+
+/**
+ A region to capture. Overrides the center coordinate
+ in the mapCamera options if set
+ */
+@property (nonatomic) MGLCoordinateBounds region;
+
+#pragma mark - Configuring the image
+
+/**
+ The size of the output image. Minimum is 64x64
+ */
+@property (nonatomic, readonly) CGSize size;
+
+/**
+ The scale of the output image. Defaults to the main screen scale.
+ Minimum is 1.
+ */
+@property (nonatomic) CGFloat scale;
+
+@end
+
+/**
+ A block to processes the result or error of a snapshot request.
+
+ The result will be either an `MGLImage` or a `NSError`
+
+ @param snapshot The image that was generated or `nil` if an error occurred.
+ @param error The eror that occured or `nil` when succesful.
+ */
+typedef void (^MGLMapSnapshotCompletionHandler)(MGLImage* _Nullable snapshot, NSError* _Nullable error);
+
+/**
+ A utility object for capturing map-based images.
+ */
+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.
+ */
+- (void)cancel;
+
+/**
+ Indicates whether as 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..c81fd39c4a
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -0,0 +1,163 @@
+#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"
+
+#if TARGET_OS_IPHONE
+#import "UIImage+MGLAdditions.h"
+#else
+#import "NSImage+MGLAdditions.h"
+#endif
+
+@implementation MGLMapSnapshotOptions
+
+- (instancetype _Nonnull)initWithStyleURL:(NSURL* _Nonnull)styleURL camera:(MGLMapCamera*)camera size:(CGSize) size;
+{
+ self = [super init];
+ if (self) {
+ _styleURL = styleURL;
+ _size = size;
+ _camera = camera;
+#if TARGET_OS_IPHONE
+ _scale = [UIScreen mainScreen].scale;
+#else
+ _scale = [NSScreen mainScreen].backingScaleFactor;
+#endif
+
+ }
+ return self;
+}
+
+@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) {
+ _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
+ mbgl::Size size = {
+ static_cast<uint32_t>(MAX(options.size.width, 64)),
+ static_cast<uint32_t>(MAX(options.size.height, 64))
+ };
+
+ 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.zoom);
+ cameraOptions.pitch = MAX(0, options.camera.pitch);
+
+ // Region
+ mbgl::optional<mbgl::LatLngBounds> region;
+ if (!MGLCoordinateBoundsIsEmpty(options.region)) {
+ region = MGLLatLngBoundsFromCoordinateBounds(options.region);
+ }
+
+ // Create the snapshotter
+ mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, region);
+ }
+ 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]) {
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"Already started this snapshotter"};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
+ dispatch_async(queue, ^{
+ completion(nil, error);
+ });
+ return;
+ }
+
+ _loading = true;
+
+ dispatch_async(queue, ^{
+ snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image) {
+ _loading = false;
+ if (mbglError) {
+ NSString *description = @(mbgl::util::toString(mbglError).c_str());
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ completion(nil, error);
+ });
+ } else {
+ MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image)];
+
+ // 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];
+
+ UIGraphicsBeginImageContext(mglImage.size);
+
+ [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)];
+ [logoImage drawInRect:CGRectMake(8, mglImage.size.height - (8 + logoImage.size.height), logoImage.size.width,logoImage.size.height)];
+ UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
+
+ UIGraphicsEndImageContext();
+#else
+ NSImage *logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox" ofType:@"pdf"]];
+ NSImage *compositedImage = mglImage;
+
+ [compositedImage lockFocus];
+ [logoImage drawInRect:CGRectMake(8, 8, logoImage.size.width,logoImage.size.height)];
+ [compositedImage unlockFocus];
+#endif
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ completion(compositedImage, nil);
+ });
+ });
+ }
+ });
+ mbglMapSnapshotter->snapshot(snapshotCallback->self());
+ });
+}
+
+- (void)cancel;
+{
+ snapshotCallback.reset();
+ mbglMapSnapshotter.reset();
+}
+
+@end
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/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h
index 16f134adb1..b009f893b3 100644
--- a/platform/darwin/src/MGLOfflineStorage.h
+++ b/platform/darwin/src/MGLOfflineStorage.h
@@ -154,6 +154,8 @@ typedef NS_ENUM(NSUInteger, MGLResourceKind) {
/** JSON part of a sprite sheet. It is constructed of the prefix in
https://www.mapbox.com/mapbox-gl-js/style-spec/#root-sprite and a JSON file extension. */
MGLResourceKindSpriteJSON,
+ /** Image data for a georeferenced image source. **/
+ MGLResourceKindImage,
};
/**
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 81774ad3cb..7085aa58e5 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -10,8 +10,14 @@
#import "NSBundle+MGLAdditions.h"
#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>
+#include <memory>
+
static NSString * const MGLOfflineStorageFileName = @"cache.db";
static NSString * const MGLOfflineStorageFileName3_2_0_beta_1 = @"offline.db";
@@ -36,7 +42,9 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
@end
-@implementation MGLOfflineStorage
+@implementation MGLOfflineStorage {
+ std::unique_ptr<mbgl::Actor<mbgl::ResourceTransform>> _mbglResourceTransform;
+}
+ (instancetype)sharedOfflineStorage {
static dispatch_once_t onceToken;
@@ -74,7 +82,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
- (void)setDelegate:(id<MGLOfflineStorageDelegate>)newValue {
_delegate = newValue;
if ([self.delegate respondsToSelector:@selector(offlineStorage:URLForResourceOfKind:withURL:)]) {
- _mbglFileSource->setResourceTransform([offlineStorage = self](auto kind_, 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()
@@ -99,6 +107,9 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
case mbgl::Resource::Kind::SpriteJSON:
kind = MGLResourceKindSpriteJSON;
break;
+ case mbgl::Resource::Kind::Image:
+ kind = MGLResourceKindImage;
+ break;
case mbgl::Resource::Kind::Unknown:
kind = MGLResourceKindUnknown;
break;
@@ -109,8 +120,11 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
withURL:url];
return url.absoluteString.UTF8String;
});
+
+ _mbglFileSource->setResourceTransform(_mbglResourceTransform->self());
} else {
- _mbglFileSource->setResourceTransform(nullptr);
+ _mbglResourceTransform.reset();
+ _mbglFileSource->setResourceTransform({});
}
}
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.h b/platform/darwin/src/MGLOpenGLStyleLayer.h
index bdad5f9d07..0b494e8062 100644
--- a/platform/darwin/src/MGLOpenGLStyleLayer.h
+++ b/platform/darwin/src/MGLOpenGLStyleLayer.h
@@ -8,6 +8,7 @@
NS_ASSUME_NONNULL_BEGIN
@class MGLMapView;
+@class MGLStyle;
typedef struct MGLStyleLayerDrawingContext {
CGSize size;
@@ -21,7 +22,7 @@ typedef struct MGLStyleLayerDrawingContext {
MGL_EXPORT
@interface MGLOpenGLStyleLayer : MGLStyleLayer
-@property (nonatomic, weak, readonly) MGLMapView *mapView;
+@property (nonatomic, weak, readonly) MGLStyle *style;
- (instancetype)initWithIdentifier:(NSString *)identifier;
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.mm b/platform/darwin/src/MGLOpenGLStyleLayer.mm
index 39eda758eb..36a3c20c97 100644
--- a/platform/darwin/src/MGLOpenGLStyleLayer.mm
+++ b/platform/darwin/src/MGLOpenGLStyleLayer.mm
@@ -4,7 +4,6 @@
#import "MGLStyle_Private.h"
#import "MGLStyleLayer_Private.h"
-#include <mbgl/map/map.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/math/wrap.hpp>
@@ -17,7 +16,7 @@
*/
void MGLPrepareCustomStyleLayer(void *context) {
MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
- [layer didMoveToMapView:layer.mapView];
+ [layer didMoveToMapView:layer.style.mapView];
}
/**
@@ -37,7 +36,7 @@ void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRender
.pitch = static_cast<CGFloat>(params.pitch),
.fieldOfView = static_cast<CGFloat>(params.fieldOfView),
};
- [layer drawInMapView:layer.mapView withContext:drawingContext];
+ [layer drawInMapView:layer.style.mapView withContext:drawingContext];
}
/**
@@ -49,7 +48,7 @@ void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRender
*/
void MGLFinishCustomStyleLayer(void *context) {
MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
- [layer willMoveFromMapView:layer.mapView];
+ [layer willMoveFromMapView:layer.style.mapView];
}
/**
@@ -75,12 +74,12 @@ void MGLFinishCustomStyleLayer(void *context) {
@property (nonatomic, readonly) mbgl::style::CustomLayer *rawLayer;
/**
- The map view whose style currently contains the layer.
+ The style currently containing the layer.
- If the layer is not currently part of any map view’s style, this property is
+ If the layer is not currently part of any style, this property is
set to `nil`.
*/
-@property (nonatomic, weak, readwrite) MGLMapView *mapView;
+@property (nonatomic, weak, readwrite) MGLStyle *style;
@end
@@ -112,24 +111,24 @@ void MGLFinishCustomStyleLayer(void *context) {
#pragma mark - Adding to and removing from a map view
-- (void)setMapView:(MGLMapView *)mapView {
- if (_mapView && mapView) {
+- (void)setStyle:(MGLStyle *)style {
+ if (_style && style) {
[NSException raise:@"MGLLayerReuseException"
format:@"%@ cannot be added to more than one MGLStyle at a time.", self];
}
- _mapView.style.openGLLayers[self.identifier] = nil;
- _mapView = mapView;
- _mapView.style.openGLLayers[self.identifier] = self;
+ _style.openGLLayers[self.identifier] = nil;
+ _style = style;
+ _style.openGLLayers[self.identifier] = self;
}
-- (void)addToMapView:(MGLMapView *)mapView belowLayer:(MGLStyleLayer *)otherLayer {
- self.mapView = mapView;
- [super addToMapView:mapView belowLayer:otherLayer];
+- (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer {
+ self.style = style;
+ [super addToStyle:style belowLayer:otherLayer];
}
-- (void)removeFromMapView:(MGLMapView *)mapView {
- [super removeFromMapView:mapView];
- self.mapView = nil;
+- (void)removeFromStyle:(MGLStyle *)style {
+ [super removeFromStyle:style];
+ self.style = nil;
}
/**
@@ -193,7 +192,7 @@ void MGLFinishCustomStyleLayer(void *context) {
causing the `-drawInMapView:withContext:` method to be called.
*/
- (void)setNeedsDisplay {
- [self.mapView setNeedsGLDisplay];
+ [self.style.mapView setNeedsGLDisplay];
}
@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/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index 023a81bba8..571cbdcc62 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -1,5 +1,6 @@
#import "MGLShapeSource_Private.h"
+#import "MGLStyle_Private.h"
#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
#import "MGLFeature_Private.h"
@@ -10,6 +11,7 @@
#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";
@@ -97,7 +99,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
std::vector<mbgl::Feature> features;
if (self.mapView) {
- features = self.mapView.mbglMap->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
+ features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
}
return MGLFeaturesFromMBGLFeatures(features);
}
diff --git a/platform/darwin/src/MGLSource.h b/platform/darwin/src/MGLSource.h
index a504a01791..8d8c936833 100644
--- a/platform/darwin/src/MGLSource.h
+++ b/platform/darwin/src/MGLSource.h
@@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
add and remove sources dynamically using methods such as
`-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
- Create instances of `MGLShapeSource` and the concrete subclasses of
+ 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.
diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm
index 7bab90e9de..6d57e14e8c 100644
--- a/platform/darwin/src/MGLSource.mm
+++ b/platform/darwin/src/MGLSource.mm
@@ -1,6 +1,8 @@
#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>
@@ -48,16 +50,16 @@
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, mapView.style];
+ "to the style more than once is invalid.", self, mapView.style];
}
-
+
_mapView = mapView;
- mapView.mbglMap->addSource(std::move(_pendingSource));
+ _mapView.style.rawStyle->addSource(std::move(_pendingSource));
}
- (void)removeFromMapView:(MGLMapView *)mapView {
- if (self.rawSource == mapView.mbglMap->getSource(self.identifier.UTF8String)) {
- _pendingSource = mapView.mbglMap->removeSource(self.identifier.UTF8String);
+ 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 13a3dd5a30..d7d1f66641 100644
--- a/platform/darwin/src/MGLSource_Private.h
+++ b/platform/darwin/src/MGLSource_Private.h
@@ -45,7 +45,6 @@ struct SourceWrapper {
/**
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`.
*/
@@ -53,7 +52,6 @@ struct SourceWrapper {
/**
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::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
@@ -65,7 +63,6 @@ struct SourceWrapper {
/**
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.
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index 4bf1f6a4dc..0b360de8fc 100644
--- a/platform/darwin/src/MGLStyle.h
+++ b/platform/darwin/src/MGLStyle.h
@@ -32,51 +32,14 @@ NS_ASSUME_NONNULL_BEGIN
static MGL_EXPORT const NSInteger MGLStyleDefaultVersion = 10;
/**
- An `MGLStyle` object represents the active map style of an `MGLMapView`. A
- style defines both the map’s content and every aspect of its appearance. Styles
- can be designed in
- <a href="https://www.mapbox.com/studio/">Mapbox Studio</a> and hosted on
- mapbox.com. `MGLStyle` provides methods for inspecting and manipulating a style
- dynamically, with classes and properties that parallel the style JSON format
- defined by the
- <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/">Mapbox Style Specification</a>.
-
- You set a map view’s active style using the `MGLMapView.styleURL` property.
- `MGLStyle` provides a set of convenience methods that return the URLs of
- <a href="https://www.mapbox.com/maps/">popular Mapbox-designed styles</a>.
- Once the `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]` or
- `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` method is called, signaling
- that the style has finished loading, you can use the `MGLMapView.style`
- property to obtain the map view’s `MGLStyle`.
-
- A style primarily consists of the following components:
-
- * _Content sources_ supply content to be shown on the map. Use methods such as
- `-sourceWithIdentifier:` and `-addSource:` to configure the style’s content
- sources, which are represented by `MGLSource` objects.
- * _Style layers_ manage the layout and appearance of content at specific
- z-indices in the style. Most kinds of style layers display content provided
- by a content source. Use methods such as `-layerWithIdentifier:` and
- `-addLayer:` to configure the style’s layers, which are represented by
- `MGLStyleLayer` objects.
- * _Style images_ are used as icons and patterns in style layers. Use the
- `-setImage:forName:` method to register an image as a style image.
- (Annotations are represented by annotation images rather than style images.
- To configure an annotation’s appearance, use the
- `-[MGLMapViewDelegate mapView:imageForAnnotation:]` method.)
- * The style’s _light_ is the light source affecting any 3D extruded fills.
- Use the `light` property to configure the style’s light, which is represented
- by an `MGLLight` object.
-
- The `MGLStyle`, `MGLSource`, `MGLStyleLayer`, and `MGLLight` classes are
- collectively known as the _runtime styling API_. The active style influences a
- related API, visible feature querying, which is available through methods such
- as `-[MGLMapView visibleFeaturesInRect:]`.
-
- Some terminology differs between the Mapbox Style Specification and the various
- classes associated with `MGLStyle`. Consult the
- “[Information for Style Authors](../for-style-authors.html)” guide for an
- overview of these differences.
+ The proxy object for the current map style.
+
+ MGLStyle provides a set of convenience methods for changing Mapbox
+ default styles using `-[MGLMapView styleURL]`.
+ <a href="https://www.mapbox.com/maps/">Learn more about Mapbox default styles</a>.
+
+ It is also possible to directly manipulate the current map style
+ via `-[MGLMapView style]` by updating the style's data sources or layers.
@note Wait until the map style has finished loading before modifying a map's
style via any of the `MGLStyle` instance methods below. You can use the
@@ -493,40 +456,24 @@ MGL_EXPORT
#pragma mark Managing Style Classes
/**
- Currently active style classes, represented as an array of string identifiers.
+ 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 will be removed in a future release.")));
+@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional.")));
/**
- Returns a Boolean value indicating whether the style class with the given
- identifier is currently active.
-
- @param styleClass The style class to query for.
- @return Whether the style class is currently active.
+ Support for style classes has been removed. This method always returns NO.
*/
-- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method will be removed in a future release.")));
+- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
/**
- Activates the style class with the given identifier.
-
- @param styleClass The style class to activate.
+ Support for style classes has been removed. This method is a no-op.
*/
-- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method will be removed in a future release.")));
+- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
/**
- Deactivates the style class with the given identifier.
-
- @note Style class names 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 like
- `+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`’s “Style URL”
- inspectable in Interface Builder, or a manually constructed `NSURL`. This
- approach also avoids style class name changes that will occur in the default
- style over time.
-
- @param styleClass The style class to deactivate.
+ Support for style classes has been removed. This method is a no-op.
*/
-- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method will be removed in a future release.")));
+- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
#pragma mark Managing a Style’s Images
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 23e75f211c..52efc7a85a 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -2,6 +2,7 @@
#import "MGLMapView_Private.h"
#import "MGLStyleLayer.h"
+#import "MGLStyleLayer_Private.h"
#import "MGLFillStyleLayer.h"
#import "MGLFillExtrusionStyleLayer.h"
#import "MGLLineStyleLayer.h"
@@ -11,24 +12,21 @@
#import "MGLBackgroundStyleLayer.h"
#import "MGLOpenGLStyleLayer.h"
-#import "MGLStyle_Private.h"
-#import "MGLStyleLayer_Private.h"
+#import "MGLSource.h"
#import "MGLSource_Private.h"
#import "MGLLight_Private.h"
-
-#import "NSDate+MGLAdditions.h"
-
-#import "MGLSource.h"
#import "MGLTileSource_Private.h"
#import "MGLVectorSource.h"
#import "MGLVectorSource+MGLAdditions.h"
#import "MGLRasterSource.h"
#import "MGLShapeSource.h"
+#import "MGLImageSource.h"
#import "MGLAttributionInfo_Private.h"
#include <mbgl/map/map.hpp>
#include <mbgl/util/default_styles.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/light.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
@@ -42,6 +40,9 @@
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/image_source.hpp>
+
+#import "NSDate+MGLAdditions.h"
#if TARGET_OS_IPHONE
#import "UIImage+MGLAdditions.h"
@@ -73,7 +74,8 @@
@interface MGLStyle()
-@property (nonatomic, readwrite, weak) MGLMapView *mapView;
+@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;
@@ -163,9 +165,10 @@ static NSURL *MGLStyleURL_trafficNight;
#pragma mark -
-- (instancetype)initWithMapView:(MGLMapView *)mapView {
+- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView {
if (self = [super init]) {
_mapView = mapView;
+ _rawStyle = rawStyle;
_openGLLayers = [NSMutableDictionary dictionary];
_localizedLayersByIdentifier = [NSMutableDictionary dictionary];
}
@@ -173,18 +176,18 @@ static NSURL *MGLStyleURL_trafficNight;
}
- (NSURL *)URL {
- return [NSURL URLWithString:@(self.mapView.mbglMap->getStyleURL().c_str())];
+ return [NSURL URLWithString:@(self.rawStyle->getURL().c_str())];
}
- (NSString *)name {
- std::string name = self.mapView.mbglMap->getStyleName();
+ std::string name = self.rawStyle->getName();
return name.empty() ? nil : @(name.c_str());
}
#pragma mark Sources
- (NS_SET_OF(__kindof MGLSource *) *)sources {
- auto rawSources = self.mapView.mbglMap->getSources();
+ auto rawSources = self.rawStyle->getSources();
NS_MUTABLE_SET_OF(__kindof MGLSource *) *sources = [NSMutableSet setWithCapacity:rawSources.size()];
for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) {
MGLSource *source = [self sourceFromMBGLSource:*rawSource];
@@ -203,8 +206,7 @@ static NSURL *MGLStyleURL_trafficNight;
}
- (NSUInteger)countOfSources {
- auto rawSources = self.mapView.mbglMap->getSources();
- return rawSources.size();
+ return self.rawStyle->getSources().size();
}
- (MGLSource *)memberOfSources:(MGLSource *)object {
@@ -213,7 +215,7 @@ static NSURL *MGLStyleURL_trafficNight;
- (MGLSource *)sourceWithIdentifier:(NSString *)identifier
{
- auto rawSource = self.mapView.mbglMap->getSource(identifier.UTF8String);
+ auto rawSource = self.rawStyle->getSource(identifier.UTF8String);
return rawSource ? [self sourceFromMBGLSource:rawSource] : nil;
}
@@ -231,6 +233,8 @@ static NSURL *MGLStyleURL_trafficNight;
return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource mapView:self.mapView];
} else if (auto rasterSource = rawSource->as<mbgl::style::RasterSource>()) {
return [[MGLRasterSource alloc] initWithRawSource:rasterSource mapView:self.mapView];
+ } else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) {
+ return [[MGLImageSource alloc] initWithRawSource:imageSource mapView:self.mapView];
} else {
return [[MGLSource alloc] initWithRawSource:rawSource mapView:self.mapView];
}
@@ -267,7 +271,7 @@ static NSURL *MGLStyleURL_trafficNight;
// It’d be incredibly convenient to use -sources here, but this operation
// depends on the sources being sorted in ascending order by creation, as
// with the std::vector used in mbgl.
- auto rawSources = self.mapView.mbglMap->getSources();
+ auto rawSources = self.rawStyle->getSources();
NSMutableArray *infos = [NSMutableArray arrayWithCapacity:rawSources.size()];
for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) {
MGLTileSource *source = (MGLTileSource *)[self sourceFromMBGLSource:*rawSource];
@@ -285,7 +289,7 @@ static NSURL *MGLStyleURL_trafficNight;
- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers
{
- auto layers = self.mapView.mbglMap->getLayers();
+ auto layers = self.rawStyle->getLayers();
NS_MUTABLE_ARRAY_OF(__kindof MGLStyleLayer *) *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()];
for (auto layer : layers) {
MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer];
@@ -305,12 +309,12 @@ static NSURL *MGLStyleURL_trafficNight;
- (NSUInteger)countOfLayers
{
- return self.mapView.mbglMap->getLayers().size();
+ return self.rawStyle->getLayers().size();
}
- (MGLStyleLayer *)objectInLayersAtIndex:(NSUInteger)index
{
- auto layers = self.mapView.mbglMap->getLayers();
+ auto layers = self.rawStyle->getLayers();
if (index >= layers.size()) {
[NSException raise:NSRangeException
format:@"No style layer at index %lu.", (unsigned long)index];
@@ -322,7 +326,7 @@ static NSURL *MGLStyleURL_trafficNight;
- (void)getLayers:(MGLStyleLayer **)buffer range:(NSRange)inRange
{
- auto layers = self.mapView.mbglMap->getLayers();
+ auto layers = self.rawStyle->getLayers();
if (NSMaxRange(inRange) > layers.size()) {
[NSException raise:NSRangeException
format:@"Style layer range %@ is out of bounds.", NSStringFromRange(inRange)];
@@ -342,21 +346,21 @@ static NSURL *MGLStyleURL_trafficNight;
@"Make sure the style layer was created as a member of a concrete subclass of MGLStyleLayer.",
styleLayer];
}
- auto layers = self.mapView.mbglMap->getLayers();
+ auto layers = self.rawStyle->getLayers();
if (index > layers.size()) {
[NSException raise:NSRangeException
format:@"Cannot insert style layer at out-of-bounds index %lu.", (unsigned long)index];
} else if (index == 0) {
try {
MGLStyleLayer *sibling = layers.size() ? [self layerFromMBGLLayer:layers.at(0)] : nil;
- [styleLayer addToMapView:self.mapView belowLayer:sibling];
+ [styleLayer addToStyle:self belowLayer:sibling];
} catch (const std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
}
} else {
try {
MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index)];
- [styleLayer addToMapView:self.mapView belowLayer:sibling];
+ [styleLayer addToStyle:self belowLayer:sibling];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
}
@@ -365,14 +369,14 @@ static NSURL *MGLStyleURL_trafficNight;
- (void)removeObjectFromLayersAtIndex:(NSUInteger)index
{
- auto layers = self.mapView.mbglMap->getLayers();
+ auto layers = self.rawStyle->getLayers();
if (index >= layers.size()) {
[NSException raise:NSRangeException
format:@"Cannot remove style layer at out-of-bounds index %lu.", (unsigned long)index];
}
auto layer = layers.at(index);
MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer];
- [styleLayer removeFromMapView:self.mapView];
+ [styleLayer removeFromStyle:self];
}
- (MGLStyleLayer *)layerFromMBGLLayer:(mbgl::style::Layer *)rawLayer
@@ -407,7 +411,7 @@ static NSURL *MGLStyleURL_trafficNight;
- (MGLStyleLayer *)layerWithIdentifier:(NSString *)identifier
{
- auto mbglLayer = self.mapView.mbglMap->getLayer(identifier.UTF8String);
+ auto mbglLayer = self.rawStyle->getLayer(identifier.UTF8String);
return mbglLayer ? [self layerFromMBGLLayer:mbglLayer] : nil;
}
@@ -420,7 +424,7 @@ static NSURL *MGLStyleURL_trafficNight;
layer];
}
[self willChangeValueForKey:@"layers"];
- [layer removeFromMapView:self.mapView];
+ [layer removeFromStyle:self];
[self didChangeValueForKey:@"layers"];
}
@@ -434,7 +438,7 @@ static NSURL *MGLStyleURL_trafficNight;
}
[self willChangeValueForKey:@"layers"];
try {
- [layer addToMapView:self.mapView belowLayer:nil];
+ [layer addToStyle:self belowLayer:nil];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
}
@@ -463,7 +467,7 @@ static NSURL *MGLStyleURL_trafficNight;
}
[self willChangeValueForKey:@"layers"];
try {
- [layer addToMapView:self.mapView belowLayer:sibling];
+ [layer addToStyle:self belowLayer:sibling];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
}
@@ -486,7 +490,7 @@ static NSURL *MGLStyleURL_trafficNight;
sibling];
}
- auto layers = self.mapView.mbglMap->getLayers();
+ auto layers = self.rawStyle->getLayers();
std::string siblingIdentifier = sibling.identifier.UTF8String;
NSUInteger index = 0;
for (auto layer : layers) {
@@ -505,14 +509,14 @@ static NSURL *MGLStyleURL_trafficNight;
sibling];
} else if (index + 1 == layers.size()) {
try {
- [layer addToMapView:self.mapView belowLayer:nil];
+ [layer addToStyle:self belowLayer:nil];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
}
} else {
MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index + 1)];
try {
- [layer addToMapView:self.mapView belowLayer:sibling];
+ [layer addToStyle:self belowLayer:sibling];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()];
}
@@ -524,60 +528,32 @@ static NSURL *MGLStyleURL_trafficNight;
- (NS_ARRAY_OF(NSString *) *)styleClasses
{
- const std::vector<std::string> &appliedClasses = self.mapView.mbglMap->getClasses();
-
- NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:appliedClasses.size()];
-
- for (auto appliedClass : appliedClasses) {
- [returnArray addObject:@(appliedClass.c_str())];
- }
-
- return returnArray;
+ return @[];
}
- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses
{
- [self setStyleClasses:appliedClasses transitionDuration:0];
}
- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration
{
- std::vector<std::string> newAppliedClasses;
-
- for (NSString *appliedClass in appliedClasses)
- {
- newAppliedClasses.push_back([appliedClass UTF8String]);
- }
-
- mbgl::style::TransitionOptions transition { { MGLDurationFromTimeInterval(transitionDuration) } };
- self.mapView.mbglMap->setTransitionOptions(transition);
- self.mapView.mbglMap->setClasses(newAppliedClasses);
}
- (NSUInteger)countOfStyleClasses {
- const auto &classes = self.mapView.mbglMap->getClasses();
- return classes.size();
+ return 0;
}
- (BOOL)hasStyleClass:(NSString *)styleClass
{
- return styleClass && self.mapView.mbglMap->hasClass([styleClass UTF8String]);
+ return NO;
}
- (void)addStyleClass:(NSString *)styleClass
{
- if (styleClass)
- {
- self.mapView.mbglMap->addClass([styleClass UTF8String]);
- }
}
- (void)removeStyleClass:(NSString *)styleClass
{
- if (styleClass)
- {
- self.mapView.mbglMap->removeClass([styleClass UTF8String]);
- }
}
#pragma mark Style images
@@ -593,7 +569,7 @@ static NSURL *MGLStyleURL_trafficNight;
format:@"Cannot assign image %@ to a nil name.", image];
}
- self.mapView.mbglMap->addImage([name UTF8String], image.mgl_styleImage);
+ self.rawStyle->addImage([image mgl_styleImageWithIdentifier:name]);
}
- (void)removeImageForName:(NSString *)name
@@ -603,7 +579,7 @@ static NSURL *MGLStyleURL_trafficNight;
format:@"Cannot remove image with nil name."];
}
- self.mapView.mbglMap->removeImage([name UTF8String]);
+ self.rawStyle->removeImage([name UTF8String]);
}
- (MGLImage *)imageForName:(NSString *)name
@@ -613,7 +589,7 @@ static NSURL *MGLStyleURL_trafficNight;
format:@"Cannot get image with nil name."];
}
- auto styleImage = self.mapView.mbglMap->getImage([name UTF8String]);
+ auto styleImage = self.rawStyle->getImage([name UTF8String]);
return styleImage ? [[MGLImage alloc] initWithMGLStyleImage:styleImage] : nil;
}
@@ -621,17 +597,17 @@ static NSURL *MGLStyleURL_trafficNight;
- (void)setTransition:(MGLTransition)transition
{
- auto transitionOptions = self.mapView.mbglMap->getTransitionOptions();
+ auto transitionOptions = self.rawStyle->getTransitionOptions();
transitionOptions.duration = MGLDurationFromTimeInterval(transition.duration);
transitionOptions.delay = MGLDurationFromTimeInterval(transition.delay);
- self.mapView.mbglMap->setTransitionOptions(transitionOptions);
+ self.rawStyle->setTransitionOptions(transitionOptions);
}
- (MGLTransition)transition
{
MGLTransition transition;
- const mbgl::style::TransitionOptions transitionOptions = self.mapView.mbglMap->getTransitionOptions();
+ const mbgl::style::TransitionOptions transitionOptions = self.rawStyle->getTransitionOptions();
transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
@@ -644,12 +620,12 @@ static NSURL *MGLStyleURL_trafficNight;
- (void)setLight:(MGLLight *)light
{
std::unique_ptr<mbgl::style::Light> mbglLight = std::make_unique<mbgl::style::Light>([light mbglLight]);
- self.mapView.mbglMap->setLight(std::move(mbglLight));
+ self.rawStyle->setLight(std::move(mbglLight));
}
- (MGLLight *)light
{
- auto mbglLight = self.mapView.mbglMap->getLight();
+ auto mbglLight = self.rawStyle->getLight();
MGLLight *light = [[MGLLight alloc] initWithMBGLLight:mbglLight];
return light;
}
diff --git a/platform/darwin/src/MGLStyleLayer.mm b/platform/darwin/src/MGLStyleLayer.mm
index 4bfaea934b..6400b8fcbf 100644
--- a/platform/darwin/src/MGLStyleLayer.mm
+++ b/platform/darwin/src/MGLStyleLayer.mm
@@ -1,7 +1,7 @@
#import "MGLStyleLayer_Private.h"
-#import "MGLMapView_Private.h"
+#import "MGLStyle_Private.h"
-#include <mbgl/map/map.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/layer.hpp>
@interface MGLStyleLayer ()
@@ -30,26 +30,26 @@
return self;
}
-- (void)addToMapView:(MGLMapView *)mapView belowLayer:(MGLStyleLayer *)otherLayer
+- (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer
{
if (_pendingLayer == nullptr) {
[NSException raise:@"MGLRedundantLayerException"
format:@"This instance %@ was already added to %@. Adding the same layer instance " \
- "to the style more than once is invalid.", self, mapView.style];
+ "to the style more than once is invalid.", self, style];
}
if (otherLayer) {
const mbgl::optional<std::string> belowLayerId{otherLayer.identifier.UTF8String};
- mapView.mbglMap->addLayer(std::move(_pendingLayer), belowLayerId);
+ style.rawStyle->addLayer(std::move(_pendingLayer), belowLayerId);
} else {
- mapView.mbglMap->addLayer(std::move(_pendingLayer));
+ style.rawStyle->addLayer(std::move(_pendingLayer));
}
}
-- (void)removeFromMapView:(MGLMapView *)mapView
+- (void)removeFromStyle:(MGLStyle *)style
{
- if (self.rawLayer == mapView.mbglMap->getLayer(self.identifier.UTF8String)) {
- _pendingLayer = mapView.mbglMap->removeLayer(self.identifier.UTF8String);
+ if (self.rawLayer == style.rawStyle->getLayer(self.identifier.UTF8String)) {
+ _pendingLayer = style.rawStyle->removeLayer(self.identifier.UTF8String);
}
}
diff --git a/platform/darwin/src/MGLStyleLayer_Private.h b/platform/darwin/src/MGLStyleLayer_Private.h
index ed8ec31755..9bee013c3d 100644
--- a/platform/darwin/src/MGLStyleLayer_Private.h
+++ b/platform/darwin/src/MGLStyleLayer_Private.h
@@ -34,7 +34,7 @@ struct LayerWrapper {
} \
} while (NO);
-@class MGLMapView;
+@class MGLStyle;
@interface MGLStyleLayer (Private)
@@ -69,7 +69,7 @@ struct LayerWrapper {
`mbgl::Map` and this object no longer has an active unique_ptr reference to the
`mbgl::style::Layer`.
*/
-- (void)addToMapView:(MGLMapView *)mapView belowLayer:(nullable MGLStyleLayer *)otherLayer;
+- (void)addToStyle:(MGLStyle *)style belowLayer:(nullable MGLStyleLayer *)otherLayer;
/**
Removes the mbgl style layer that this object represents from the mbgl map.
@@ -78,7 +78,7 @@ struct LayerWrapper {
to the `MGLStyleLayer` instance and the unique_ptr reference is valid again. It
is safe to add the layer back to the style after it is removed.
*/
-- (void)removeFromMapView:(MGLMapView *)mapView;
+- (void)removeFromStyle:(MGLStyle *)style;
@end
diff --git a/platform/darwin/src/MGLStyle_Private.h b/platform/darwin/src/MGLStyle_Private.h
index 23ce8fbee0..92b08e844b 100644
--- a/platform/darwin/src/MGLStyle_Private.h
+++ b/platform/darwin/src/MGLStyle_Private.h
@@ -5,15 +5,22 @@
NS_ASSUME_NONNULL_BEGIN
+namespace mbgl {
+ namespace style {
+ class Style;
+ }
+}
+
@class MGLAttributionInfo;
@class MGLMapView;
@class MGLOpenGLStyleLayer;
@interface MGLStyle (Private)
-- (instancetype)initWithMapView:(MGLMapView *)mapView;
+- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView;
@property (nonatomic, readonly, weak) MGLMapView *mapView;
+@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index b6c6372324..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.
@@ -533,7 +645,11 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconRotationAlignment;
/**
- Scale factor for icon. 1 is original size, 3 triples the size.
+ Scales the original size of the icon by the provided factor. The new point size
+ of the image will be the original point size multiplied by `iconSize`. 1 is the
+ original size; 3 triples the size of the image.
+
+ This property is measured in factor of the original icon sizes.
The default value of this property is an `MGLStyleValue` object containing an
`NSNumber` object containing the float `1`. Set this property to `nil` to reset
@@ -741,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;
@@ -881,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;
@@ -1000,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;
@@ -1026,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;
@@ -1921,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/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm
index 5e9f4f4a6e..b1bda56f2d 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorSource.mm
@@ -3,12 +3,14 @@
#import "MGLFeature_Private.h"
#import "MGLSource_Private.h"
#import "MGLTileSource_Private.h"
+#import "MGLStyle_Private.h"
#import "MGLMapView_Private.h"
#import "NSPredicate+MGLAdditions.h"
#import "NSURL+MGLAdditions.h"
#include <mbgl/map/map.hpp>
#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/renderer/renderer.hpp>
@interface MGLVectorSource ()
@@ -63,7 +65,7 @@
std::vector<mbgl::Feature> features;
if (self.mapView) {
- features = self.mapView.mbglMap->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
+ features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
}
return MGLFeaturesFromMBGLFeatures(features);
}
diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h
index 0aaa2a337a..f3026a389f 100644
--- a/platform/darwin/src/NSValue+MGLAdditions.h
+++ b/platform/darwin/src/NSValue+MGLAdditions.h
@@ -56,6 +56,20 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (readonly) MGLCoordinateBounds MGLCoordinateBoundsValue;
+/**
+ Creates a new value object containing the specified Mapbox coordinate
+ quad structure.
+
+ @param quad The value for the new object.
+ @return A new value object that contains the coordinate quad information.
+ */
++ (instancetype)valueWithMGLCoordinateQuad:(MGLCoordinateQuad)quad;
+
+/**
+ The Mapbox coordinate quad structure representation of the value.
+ */
+- (MGLCoordinateQuad)MGLCoordinateQuadValue;
+
#pragma mark Working with Offline Map Values
/**
diff --git a/platform/darwin/src/NSValue+MGLAdditions.m b/platform/darwin/src/NSValue+MGLAdditions.m
index ef894f0eb4..1383056944 100644
--- a/platform/darwin/src/NSValue+MGLAdditions.m
+++ b/platform/darwin/src/NSValue+MGLAdditions.m
@@ -34,6 +34,16 @@
return bounds;
}
++ (instancetype)valueWithMGLCoordinateQuad:(MGLCoordinateQuad)quad {
+ return [self valueWithBytes:&quad objCType:@encode(MGLCoordinateQuad)];
+}
+
+- (MGLCoordinateQuad)MGLCoordinateQuadValue {
+ MGLCoordinateQuad quad;
+ [self getValue:&quad];
+ return quad;
+}
+
#pragma mark Offline maps
+ (NSValue *)valueWithMGLOfflinePackProgress:(MGLOfflinePackProgress)progress {
diff --git a/platform/darwin/src/headless_backend_cgl.cpp b/platform/darwin/src/headless_backend_cgl.cpp
index 6ad98f4326..3b0c3aaf35 100644
--- a/platform/darwin/src/headless_backend_cgl.cpp
+++ b/platform/darwin/src/headless_backend_cgl.cpp
@@ -51,7 +51,7 @@ gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
bool HeadlessBackend::hasDisplay() {
if (!display) {
- display.reset(new HeadlessDisplay);
+ display = HeadlessDisplay::create();
}
return bool(display);
}
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/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..2ba8f8415b 100644
--- a/platform/darwin/src/run_loop.cpp
+++ b/platform/darwin/src/run_loop.cpp
@@ -1,38 +1,32 @@
#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) {
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/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
index 3e949b0967..42c656f203 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -116,6 +116,20 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(polyline)
}
+ func testMGLImageSource() {
+ //#-example-code
+ let coordinates = MGLCoordinateQuad(
+ topLeft: CLLocationCoordinate2D(latitude: 46.437, longitude: -80.425),
+ 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")!)
+ mapView.style?.addSource(source)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.source(withIdentifier: "radar"))
+ }
+
func testMGLCircleStyleLayer() {
let population = MGLVectorSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(population)
diff --git a/platform/darwin/test/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm
index 220a837643..1c85470188 100644
--- a/platform/darwin/test/MGLGeometryTests.mm
+++ b/platform/darwin/test/MGLGeometryTests.mm
@@ -144,4 +144,23 @@
XCTAssertEqualObjects(serializedGeoJSON, geoJSON, @"MGLPointFeature should serialize as a GeoJSON point feature.");
}
+- (void)testMGLCoordinateBoundsToMGLCoordinateQuad {
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(37.936, -80.425),
+ CLLocationCoordinate2DMake(46.437, -71.516));
+
+ MGLCoordinateQuad quad = MGLCoordinateQuadFromCoordinateBounds(bounds);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:bounds.sw],
+ [NSValue valueWithMGLCoordinate:quad.bottomLeft],
+ @"Bounds southwest should be bottom left of quad.");
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:bounds.ne],
+ [NSValue valueWithMGLCoordinate:quad.topRight],
+ @"Bounds northeast should be top right of quad.");
+
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(46.437, -80.425)],
+ [NSValue valueWithMGLCoordinate:quad.topLeft],
+ @"Quad top left should be computed correctly.");
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(37.936, -71.516)],
+ [NSValue valueWithMGLCoordinate:quad.bottomRight],
+ @"Quad bottom right should be computed correctly.");
+}
@end
diff --git a/platform/darwin/test/MGLImageSourceTests.m b/platform/darwin/test/MGLImageSourceTests.m
new file mode 100644
index 0000000000..38fcd38709
--- /dev/null
+++ b/platform/darwin/test/MGLImageSourceTests.m
@@ -0,0 +1,42 @@
+#import <XCTest/XCTest.h>
+
+#import <Mapbox/Mapbox.h>
+
+@interface MGLImageSourceTests : XCTestCase
+
+@end
+
+@implementation MGLImageSourceTests
+
+
+- (void)testMGLImageSourceWithImageURL {
+
+ MGLCoordinateQuad quad = { { 80, 37}, { 81, 37}, { 81, 39}, { 80, 39}};
+ MGLImageSource *source = [[MGLImageSource alloc] initWithIdentifier:@"source-id" coordinateQuad:quad URL:[NSURL URLWithString:@"http://host/image.png"]];
+
+ XCTAssertNotNil(source.URL);
+ XCTAssertEqualObjects(source.URL.absoluteString, @"http://host/image.png");
+ XCTAssertNil(source.image);
+}
+
+- (void)testMGLImageSourceWithImage {
+
+ NSString *imageName = @"RadarImage";
+#if TARGET_OS_IPHONE
+ MGLImage *image = [MGLImage imageNamed:imageName
+ inBundle:[NSBundle bundleForClass:[self class]]
+ compatibleWithTraitCollection:nil];
+#else
+ MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:imageName];
+#endif
+ XCTAssertNotNil(image);
+
+ MGLCoordinateQuad quad = { { 80, 37}, { 81, 37}, { 81, 39}, { 80, 39}};
+ MGLImageSource *source = [[MGLImageSource alloc] initWithIdentifier:@"source-id" coordinateQuad:quad image:image];
+
+ XCTAssertNotNil(source.image);
+ XCTAssertEqualObjects(source.image, image);
+ XCTAssertNil(source.URL);
+}
+
+@end
diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm
index 8f901cbb72..de64d57851 100644
--- a/platform/darwin/test/MGLLightTest.mm
+++ b/platform/darwin/test/MGLLightTest.mm
@@ -18,7 +18,7 @@
@implementation MGLLightTest
- (void)testProperties {
-
+
MGLTransition defaultTransition = MGLTransitionMake(0, 0);
MGLTransition transition = MGLTransitionMake(6, 3);
mbgl::style::TransitionOptions transitionOptions { { MGLDurationFromTimeInterval(6) }, { MGLDurationFromTimeInterval(3) } };
@@ -35,9 +35,7 @@
XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorViewport);
mbgl::style::PropertyValue<mbgl::style::LightAnchorType> propertyValue = { mbgl::style::LightAnchorType::Viewport };
-
light.setAnchor(propertyValue);
-
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
lightFromMGLlight = [mglLight mbglLight];
@@ -55,14 +53,12 @@
XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == defaultTransition.delay);
XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == defaultTransition.duration);
- const std::array<float, 3> positionArray = { { 6, 180, 90 } };
+ std::array<float, 3> positionArray = { { 6, 180, 90 } };
mbgl::style::Position position = { positionArray };
mbgl::style::PropertyValue<mbgl::style::Position> propertyValue = { position };
-
light.setPosition(propertyValue);
light.setPositionTransition(transitionOptions);
-
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
lightFromMGLlight = [mglLight mbglLight];
@@ -85,11 +81,9 @@
XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == defaultTransition.duration);
mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
-
light.setColor(propertyValue);
light.setColorTransition(transitionOptions);
-
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
lightFromMGLlight = [mglLight mbglLight];
@@ -112,11 +106,9 @@
XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == defaultTransition.duration);
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
-
light.setIntensity(propertyValue);
light.setIntensityTransition(transitionOptions);
-
mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
lightFromMGLlight = [mglLight mbglLight];
@@ -131,10 +123,10 @@
- (void)testValueAdditions {
MGLSphericalPosition position = MGLSphericalPositionMake(1.15, 210, 30);
-
- XCTAssertEqual(@(position).MGLSphericalPositionValue.radial, position.radial);
- XCTAssertEqual(@(position).MGLSphericalPositionValue.azimuthal, position.azimuthal);
- XCTAssertEqual(@(position).MGLSphericalPositionValue.polar, position.polar);
+
+ XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.radial, position.radial);
+ XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.azimuthal, position.azimuthal);
+ XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.polar, position.polar);
XCTAssertEqual([NSValue valueWithMGLLightAnchor:MGLLightAnchorMap].MGLLightAnchorValue, MGLLightAnchorMap);
XCTAssertEqual([NSValue valueWithMGLLightAnchor:MGLLightAnchorViewport].MGLLightAnchorValue, MGLLightAnchorViewport);
}
diff --git a/platform/darwin/test/MGLLightTest.mm.ejs b/platform/darwin/test/MGLLightTest.mm.ejs
index c1904d5ab8..5b1f27d8d1 100644
--- a/platform/darwin/test/MGLLightTest.mm.ejs
+++ b/platform/darwin/test/MGLLightTest.mm.ejs
@@ -22,7 +22,7 @@
@implementation MGLLightTest
- (void)testProperties {
-
+
MGLTransition defaultTransition = MGLTransitionMake(0, 0);
MGLTransition transition = MGLTransitionMake(6, 3);
mbgl::style::TransitionOptions transitionOptions { { MGLDurationFromTimeInterval(6) }, { MGLDurationFromTimeInterval(3) } };
@@ -48,17 +48,17 @@
<% } -%>
<% if (property.type == "array") { -%>
- const std::array<float, 3> positionArray = { { 6, 180, 90 } };
+ 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];
@@ -76,7 +76,7 @@
- (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/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm
index 1e92959520..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
@@ -713,7 +708,7 @@
MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
layer.lineWidth = constantStyleValue;
- mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
@"Setting lineWidth to a constant value should update line-width.");
XCTAssertEqualObjects(layer.lineWidth, constantStyleValue,
@@ -730,6 +725,29 @@
XCTAssertEqualObjects(layer.lineWidth, functionStyleValue,
@"lineWidth should round-trip camera functions.");
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.lineWidth = functionStyleValue;
+
+ mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
+ @"Setting lineWidth to a source function should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, functionStyleValue,
+ @"lineWidth should round-trip source functions.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.lineWidth = 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->getLineWidth(), propertyValue,
+ @"Setting lineWidth to a composite function should update line-width.");
+ XCTAssertEqualObjects(layer.lineWidth, functionStyleValue,
+ @"lineWidth should round-trip composite functions.");
layer.lineWidth = nil;
@@ -737,11 +755,6 @@
@"Unsetting lineWidth should return line-width to the default value.");
XCTAssertEqualObjects(layer.lineWidth, defaultStyleValue,
@"lineWidth should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineWidth = 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.lineWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
// Transition property test
layer.lineWidthTransition = transitionTest;
auto toptions = rawLayer->getLineWidthTransition();
diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
index 0814ceda2d..8f610e338c 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -213,9 +213,9 @@
MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
[self.style addSource:rasterSource];
- // Attempt to remove a shape source with the same identifier as the raster source
- MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"some-identifier" shape:nil options:nil];
- [self.style removeSource:shapeSource];
+ // Attempt to remove an image source with the same identifier as the raster source
+ MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"some-identifier" coordinateQuad: { } URL:[NSURL URLWithString:@"http://host/image.png"]];
+ [self.style removeSource:imageSource];
// The raster source should still be added
XCTAssertTrue([[self.style sourceWithIdentifier:rasterSource.identifier] isMemberOfClass:[MGLRasterSource class]]);
@@ -223,16 +223,16 @@
[self.style removeSource:rasterSource];
// Add the shape source
- [self.style addSource:shapeSource];
+ [self.style addSource:imageSource];
// Attempt to remove a vector source with the same identifer as the shape source
MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
[self.style removeSource:vectorSource];
- // The shape source should still be added
- XCTAssertTrue([[self.style sourceWithIdentifier:shapeSource.identifier] isMemberOfClass:[MGLShapeSource class]]);
+ // The image source should still be added
+ XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLImageSource class]]);
- // Remove the shape source
- [self.style removeSource:shapeSource];
+ // Remove the image source
+ [self.style removeSource:imageSource];
// Add the vector source
[self.style addSource:vectorSource];
@@ -240,7 +240,7 @@
// Attempt to remove the previously created raster source that has the same identifer as the shape source
[self.style removeSource:rasterSource];
// The vector source should still be added
- XCTAssertTrue([[self.style sourceWithIdentifier:shapeSource.identifier] isMemberOfClass:[MGLVectorSource class]]);
+ XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLVectorSource class]]);
}
- (void)testRemovingSourceInUse {
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/darwin/test/Media.xcassets/RadarImage.imageset/Contents.json b/platform/darwin/test/Media.xcassets/RadarImage.imageset/Contents.json
new file mode 100644
index 0000000000..79be9ed970
--- /dev/null
+++ b/platform/darwin/test/Media.xcassets/RadarImage.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "radar.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/darwin/test/Media.xcassets/RadarImage.imageset/radar.png b/platform/darwin/test/Media.xcassets/RadarImage.imageset/radar.png
new file mode 100644
index 0000000000..e23697f42a
--- /dev/null
+++ b/platform/darwin/test/Media.xcassets/RadarImage.imageset/radar.png
Binary files differ
diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp
index 1832818378..54dbb8d0f6 100644
--- a/platform/default/asset_file_source.cpp
+++ b/platform/default/asset_file_source.cpp
@@ -1,4 +1,5 @@
#include <mbgl/storage/asset_file_source.hpp>
+#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/thread.hpp>
@@ -8,17 +9,16 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <unistd.h>
namespace mbgl {
class AssetFileSource::Impl {
public:
- Impl(std::string root_)
+ Impl(ActorRef<Impl>, std::string root_)
: root(std::move(root_)) {
}
- void request(const std::string& url, FileSource::Callback callback) {
+ void request(const std::string& url, ActorRef<FileSourceRequest> req) {
std::string path;
if (url.size() <= 8 || url[8] == '/') {
@@ -34,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);
@@ -48,7 +48,7 @@ public:
}
}
- callback(response);
+ req.invoke(&FileSourceRequest::setResponse, response);
}
private:
@@ -56,15 +56,17 @@ private:
};
AssetFileSource::AssetFileSource(const std::string& root)
- : thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{"AssetFileSource", util::ThreadPriority::Low},
- root)) {
+ : impl(std::make_unique<util::Thread<Impl>>("AssetFileSource", root)) {
}
AssetFileSource::~AssetFileSource() = default;
std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
- return thread->invokeWithCallback(&Impl::request, resource.url, callback);
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
+
+ impl->actor().invoke(&Impl::request, resource.url, req->actor());
+
+ return std::move(req);
}
} // namespace mbgl
diff --git a/platform/default/async_task.cpp b/platform/default/async_task.cpp
index 5fa9db8d33..50891056d8 100644
--- a/platform/default/async_task.cpp
+++ b/platform/default/async_task.cpp
@@ -16,7 +16,7 @@ public:
: async(new uv_async_t),
task(std::move(fn)) {
- uv_loop_t* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle());
+ auto* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle());
if (uv_async_init(loop, async, asyncCallback) != 0) {
throw std::runtime_error("Failed to initialize async.");
}
diff --git a/platform/default/bidi.cpp b/platform/default/bidi.cpp
index a76c4bcaac..d475c387b3 100644
--- a/platform/default/bidi.cpp
+++ b/platform/default/bidi.cpp
@@ -30,7 +30,7 @@ std::u16string applyArabicShaping(const std::u16string& input) {
UErrorCode errorCode = U_ZERO_ERROR;
const int32_t outputLength =
- u_shapeArabic(mbgl::utf16char_cast<const UChar*>(input.c_str()), static_cast<int32_t>(input.size()), NULL, 0,
+ u_shapeArabic(mbgl::utf16char_cast<const UChar*>(input.c_str()), static_cast<int32_t>(input.size()), nullptr, 0,
(U_SHAPE_LETTERS_SHAPE & U_SHAPE_LETTERS_MASK) |
(U_SHAPE_TEXT_DIRECTION_LOGICAL & U_SHAPE_TEXT_DIRECTION_MASK),
&errorCode);
@@ -57,7 +57,7 @@ void BiDi::mergeParagraphLineBreaks(std::set<size_t>& lineBreakPoints) {
for (int32_t i = 0; i < paragraphCount; i++) {
UErrorCode errorCode = U_ZERO_ERROR;
int32_t paragraphEndIndex;
- ubidi_getParagraphByIndex(impl->bidiText, i, NULL, &paragraphEndIndex, NULL, &errorCode);
+ ubidi_getParagraphByIndex(impl->bidiText, i, nullptr, &paragraphEndIndex, nullptr, &errorCode);
if (U_FAILURE(errorCode)) {
throw std::runtime_error(std::string("ProcessedBiDiText::mergeParagraphLineBreaks: ") +
@@ -92,7 +92,7 @@ std::vector<std::u16string> BiDi::processText(const std::u16string& input,
UErrorCode errorCode = U_ZERO_ERROR;
ubidi_setPara(impl->bidiText, mbgl::utf16char_cast<const UChar*>(input.c_str()), static_cast<int32_t>(input.size()),
- UBIDI_DEFAULT_LTR, NULL, &errorCode);
+ UBIDI_DEFAULT_LTR, nullptr, &errorCode);
if (U_FAILURE(errorCode)) {
throw std::runtime_error(std::string("BiDi::processText: ") + u_errorName(errorCode));
diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp
index 7db0b85461..3fdb03e6b4 100644
--- a/platform/default/default_file_source.cpp
+++ b/platform/default/default_file_source.cpp
@@ -1,9 +1,11 @@
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/asset_file_source.hpp>
+#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/local_file_source.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/offline_database.hpp>
#include <mbgl/storage/offline_download.hpp>
+#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/util/platform.hpp>
#include <mbgl/util/url.hpp>
@@ -26,8 +28,15 @@ namespace mbgl {
class DefaultFileSource::Impl {
public:
- Impl(const std::string& cachePath, uint64_t maximumCacheSize)
- : offlineDatabase(cachePath, maximumCacheSize) {
+ Impl(ActorRef<Impl> self, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize)
+ : assetFileSource(assetFileSource_)
+ , 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) {
@@ -46,13 +55,13 @@ public:
return onlineFileSource.getAccessToken();
}
- void setResourceTransform(OnlineFileSource::ResourceTransform&& transform) {
+ void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
onlineFileSource.setResourceTransform(std::move(transform));
}
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(), {});
}
@@ -62,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(), {});
}
@@ -72,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(), {});
}
@@ -89,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());
@@ -104,36 +113,66 @@ public:
getDownload(regionID).setState(state);
}
- void request(AsyncRequest* req, Resource resource, Callback callback) {
- 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");
+ void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) {
+ auto callback = [ref] (const Response& res) mutable {
+ ref.invoke(&FileSourceRequest::setResponse, res);
+ };
+
+ if (isAssetURL(resource.url)) {
+ //Asset request
+ tasks[req] = assetFileSource->request(resource, callback);
+ } else if (LocalFileSource::acceptsURL(resource.url)) {
+ //Local file request
+ tasks[req] = localFileSource->request(resource, callback);
+ } else {
+ // Try the offline database
+ const bool hasPrior = resource.priorEtag || resource.priorModified ||
+ resource.priorExpires || resource.priorData;
+ 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) {
+ resource.priorModified = offlineResponse->modified;
+ resource.priorExpires = offlineResponse->expires;
+ resource.priorEtag = offlineResponse->etag;
+
+ // 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.
+ if (offlineResponse->isUsable()) {
+ callback(*offlineResponse);
+ } else if (resource.necessity == Resource::Optional) {
+ // Instead of the data that we got, return a not found error so that
+ // underlying implementations know about the fact that we couldn't find
+ // usable cache data.
+ offlineResponse->error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, "Cached resource is unusable");
+ callback(*offlineResponse);
+ } else {
+ // Since we can't return the data immediately, we'll have to hold on so that
+ // we can return it later in case we get a 304 Not Modified response.
+ resource.priorData = offlineResponse->data;
+ }
+ }
}
- if (offlineResponse) {
- revalidation.priorModified = offlineResponse->modified;
- revalidation.priorExpires = offlineResponse->expires;
- revalidation.priorEtag = offlineResponse->etag;
- callback(*offlineResponse);
+ // Get from the online file source
+ if (resource.necessity == Resource::Required) {
+ tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) mutable {
+ this->offlineDatabase->put(resource, onlineResponse);
+ callback(onlineResponse);
+ });
}
}
-
- if (resource.necessity == Resource::Required) {
- tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) {
- this->offlineDatabase.put(revalidation, onlineResponse);
- callback(onlineResponse);
- });
- }
}
void cancel(AsyncRequest* req) {
@@ -141,11 +180,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:
@@ -155,10 +198,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;
}
- OfflineDatabase offlineDatabase;
+ // shared so that destruction is done on the creating thread
+ const std::shared_ptr<FileSource> assetFileSource;
+ const std::unique_ptr<FileSource> localFileSource;
+ 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;
@@ -173,118 +219,106 @@ DefaultFileSource::DefaultFileSource(const std::string& cachePath,
DefaultFileSource::DefaultFileSource(const std::string& cachePath,
std::unique_ptr<FileSource>&& assetFileSource_,
uint64_t maximumCacheSize)
- : thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"DefaultFileSource", util::ThreadPriority::Low},
- cachePath, maximumCacheSize)),
- assetFileSource(std::move(assetFileSource_)),
- localFileSource(std::make_unique<LocalFileSource>()) {
+ : assetFileSource(std::move(assetFileSource_))
+ , impl(std::make_unique<util::Thread<Impl>>("DefaultFileSource", assetFileSource, cachePath, maximumCacheSize)) {
}
DefaultFileSource::~DefaultFileSource() = default;
void DefaultFileSource::setAPIBaseURL(const std::string& baseURL) {
- thread->invoke(&Impl::setAPIBaseURL, baseURL);
- cachedBaseURL = baseURL;
+ impl->actor().invoke(&Impl::setAPIBaseURL, baseURL);
+
+ {
+ std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
+ cachedBaseURL = baseURL;
+ }
}
-std::string DefaultFileSource::getAPIBaseURL() const {
+std::string DefaultFileSource::getAPIBaseURL() {
+ std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
return cachedBaseURL;
}
void DefaultFileSource::setAccessToken(const std::string& accessToken) {
- thread->invoke(&Impl::setAccessToken, accessToken);
- cachedAccessToken = accessToken;
+ impl->actor().invoke(&Impl::setAccessToken, accessToken);
+
+ {
+ std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
+ cachedAccessToken = accessToken;
+ }
}
-std::string DefaultFileSource::getAccessToken() const {
+std::string DefaultFileSource::getAccessToken() {
+ std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
return cachedAccessToken;
}
-void DefaultFileSource::setResourceTransform(std::function<std::string(Resource::Kind, std::string&&)> transform) {
- if (transform) {
- auto loop = util::RunLoop::Get();
- thread->invoke(&Impl::setResourceTransform, [loop, transform](Resource::Kind kind_, std::string&& url_, auto callback_) {
- return loop->invokeWithCallback([transform](Resource::Kind kind, std::string&& url, auto callback) {
- callback(transform(kind, std::move(url)));
- }, kind_, std::move(url_), callback_);
- });
- } else {
- thread->invoke(&Impl::setResourceTransform, nullptr);
- }
+void DefaultFileSource::setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
+ impl->actor().invoke(&Impl::setResourceTransform, std::move(transform));
}
std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
- class DefaultFileRequest : public AsyncRequest {
- public:
- DefaultFileRequest(Resource resource_, FileSource::Callback callback_, util::Thread<DefaultFileSource::Impl>& thread_)
- : thread(thread_),
- workRequest(thread.invokeWithCallback(&DefaultFileSource::Impl::request, this, resource_, callback_)) {
- }
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
- ~DefaultFileRequest() override {
- thread.invoke(&DefaultFileSource::Impl::cancel, this);
- }
+ req->onCancel([fs = impl->actor(), req = req.get()] () mutable { fs.invoke(&Impl::cancel, req); });
- util::Thread<DefaultFileSource::Impl>& thread;
- std::unique_ptr<AsyncRequest> workRequest;
- };
+ impl->actor().invoke(&Impl::request, req.get(), resource, req->actor());
- if (isAssetURL(resource.url)) {
- return assetFileSource->request(resource, callback);
- } else if (LocalFileSource::acceptsURL(resource.url)) {
- return localFileSource->request(resource, callback);
- } else {
- return std::make_unique<DefaultFileRequest>(resource, callback, *thread);
- }
+ return std::move(req);
}
void DefaultFileSource::listOfflineRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) {
- thread->invoke(&Impl::listRegions, callback);
+ impl->actor().invoke(&Impl::listRegions, callback);
}
void DefaultFileSource::createOfflineRegion(const OfflineRegionDefinition& definition,
const OfflineRegionMetadata& metadata,
std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) {
- thread->invoke(&Impl::createRegion, definition, metadata, callback);
+ impl->actor().invoke(&Impl::createRegion, definition, metadata, callback);
}
void DefaultFileSource::updateOfflineMetadata(const int64_t regionID,
const OfflineRegionMetadata& metadata,
std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) {
- thread->invoke(&Impl::updateMetadata, regionID, metadata, callback);
+ impl->actor().invoke(&Impl::updateMetadata, regionID, metadata, callback);
}
void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, std::function<void (std::exception_ptr)> callback) {
- thread->invoke(&Impl::deleteRegion, std::move(region), callback);
+ impl->actor().invoke(&Impl::deleteRegion, std::move(region), callback);
}
void DefaultFileSource::setOfflineRegionObserver(OfflineRegion& region, std::unique_ptr<OfflineRegionObserver> observer) {
- thread->invoke(&Impl::setRegionObserver, region.getID(), std::move(observer));
+ impl->actor().invoke(&Impl::setRegionObserver, region.getID(), std::move(observer));
}
void DefaultFileSource::setOfflineRegionDownloadState(OfflineRegion& region, OfflineRegionDownloadState state) {
- thread->invoke(&Impl::setRegionDownloadState, region.getID(), state);
+ impl->actor().invoke(&Impl::setRegionDownloadState, region.getID(), state);
}
void DefaultFileSource::getOfflineRegionStatus(OfflineRegion& region, std::function<void (std::exception_ptr, optional<OfflineRegionStatus>)> callback) const {
- thread->invoke(&Impl::getRegionStatus, region.getID(), callback);
+ impl->actor().invoke(&Impl::getRegionStatus, region.getID(), callback);
}
void DefaultFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const {
- thread->invokeSync(&Impl::setOfflineMapboxTileCountLimit, limit);
+ impl->actor().invoke(&Impl::setOfflineMapboxTileCountLimit, limit);
}
void DefaultFileSource::pause() {
- thread->pause();
+ impl->pause();
}
void DefaultFileSource::resume() {
- thread->resume();
+ impl->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) {
- thread->invokeSync(&Impl::put, resource, response);
+ impl->actor().invoke(&Impl::put, resource, response);
}
} // 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/image.cpp b/platform/default/image.cpp
index ad9d83a08d..447c6bcd66 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.cpp
@@ -12,7 +12,7 @@ PremultipliedImage decodePNG(const uint8_t*, size_t);
PremultipliedImage decodeJPEG(const uint8_t*, size_t);
PremultipliedImage decodeImage(const std::string& string) {
- const uint8_t* data = reinterpret_cast<const uint8_t*>(string.data());
+ const auto* data = reinterpret_cast<const uint8_t*>(string.data());
const size_t size = string.size();
#if !defined(__ANDROID__) && !defined(__APPLE__)
diff --git a/platform/default/jpeg_reader.cpp b/platform/default/jpeg_reader.cpp
index c5e9d880c0..5f613f9423 100644
--- a/platform/default/jpeg_reader.cpp
+++ b/platform/default/jpeg_reader.cpp
@@ -21,12 +21,12 @@ struct jpeg_stream_wrapper {
};
static void init_source(j_decompress_ptr cinfo) {
- jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
+ auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
wrap->stream->seekg(0, std::ios_base::beg);
}
static boolean fill_input_buffer(j_decompress_ptr cinfo) {
- jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
+ auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
wrap->stream->read(reinterpret_cast<char*>(&wrap->buffer[0]), BUF_SIZE);
std::streamsize size = wrap->stream->gcount();
wrap->manager.next_input_byte = wrap->buffer.data();
@@ -36,7 +36,7 @@ static boolean fill_input_buffer(j_decompress_ptr cinfo) {
static void skip(j_decompress_ptr cinfo, long count) {
if (count <= 0) return; // A zero or negative skip count should be treated as a no-op.
- jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
+ auto* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
if (wrap->manager.bytes_in_buffer > 0 && count < static_cast<long>(wrap->manager.bytes_in_buffer))
{
@@ -59,7 +59,7 @@ static void attach_stream(j_decompress_ptr cinfo, std::istream* in) {
cinfo->src = (struct jpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(jpeg_stream_wrapper));
}
- jpeg_stream_wrapper * src = reinterpret_cast<jpeg_stream_wrapper*> (cinfo->src);
+ auto * src = reinterpret_cast<jpeg_stream_wrapper*> (cinfo->src);
src->manager.init_source = init_source;
src->manager.fill_input_buffer = fill_input_buffer;
src->manager.skip_input_data = skip;
diff --git a/platform/default/local_file_source.cpp b/platform/default/local_file_source.cpp
index 93b42f5fa0..21a291d8d4 100644
--- a/platform/default/local_file_source.cpp
+++ b/platform/default/local_file_source.cpp
@@ -1,4 +1,5 @@
#include <mbgl/storage/local_file_source.hpp>
+#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/thread.hpp>
@@ -21,7 +22,9 @@ namespace mbgl {
class LocalFileSource::Impl {
public:
- void request(const std::string& url, FileSource::Callback callback) {
+ Impl(ActorRef<Impl>) {}
+
+ void request(const std::string& url, ActorRef<FileSourceRequest> req) {
// Cut off the protocol
std::string path = mbgl::util::percentDecode(url.substr(protocolLength));
@@ -44,19 +47,23 @@ public:
}
}
- callback(response);
+ req.invoke(&FileSourceRequest::setResponse, response);
}
};
LocalFileSource::LocalFileSource()
- : thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"LocalFileSource", util::ThreadPriority::Low})) {
+ : impl(std::make_unique<util::Thread<Impl>>("LocalFileSource")) {
}
LocalFileSource::~LocalFileSource() = default;
std::unique_ptr<AsyncRequest> LocalFileSource::request(const Resource& resource, Callback callback) {
- return thread->invokeWithCallback(&Impl::request, resource.url, callback);
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
+
+ impl->actor().invoke(&Impl::request, resource.url, req->actor());
+
+ return std::move(req);
}
bool LocalFileSource::acceptsURL(const std::string& url) {
diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp
index d2fbf4e4c7..edf637a560 100644
--- a/platform/default/mbgl/gl/headless_backend.cpp
+++ b/platform/default/mbgl/gl/headless_backend.cpp
@@ -1,7 +1,7 @@
#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,15 +9,26 @@
namespace mbgl {
-HeadlessBackend::HeadlessBackend() {
-}
+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();
}
@@ -41,12 +52,33 @@ void HeadlessBackend::deactivate() {
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 128b579bd2..66f861e213 100644
--- a/platform/default/mbgl/gl/headless_backend.hpp
+++ b/platform/default/mbgl/gl/headless_backend.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include <mbgl/map/backend.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
#include <memory>
#include <functional>
@@ -9,18 +9,20 @@ 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 {
- virtual ~Impl() {}
+ virtual ~Impl() = default;
virtual void activateContext() = 0;
virtual void deactivateContext() {}
};
@@ -40,7 +42,12 @@ private:
std::shared_ptr<HeadlessDisplay> display;
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.hpp b/platform/default/mbgl/gl/headless_display.hpp
index a5c95085b8..8c294655e5 100644
--- a/platform/default/mbgl/gl/headless_display.hpp
+++ b/platform/default/mbgl/gl/headless_display.hpp
@@ -6,13 +6,27 @@ namespace mbgl {
class HeadlessDisplay {
public:
- HeadlessDisplay();
+ static std::shared_ptr<HeadlessDisplay> create() {
+ static std::weak_ptr<HeadlessDisplay> instance;
+
+ auto shared = instance.lock();
+
+ if (!shared) {
+ instance = shared = std::shared_ptr<HeadlessDisplay>(new HeadlessDisplay());
+ }
+
+ return shared;
+ }
+
+
~HeadlessDisplay();
template <typename DisplayAttribute>
DisplayAttribute attribute() const;
private:
+ HeadlessDisplay();
+
class Impl;
std::unique_ptr<Impl> impl;
};
diff --git a/platform/default/mbgl/gl/headless_frontend.cpp b/platform/default/mbgl/gl/headless_frontend.cpp
new file mode 100644
index 0000000000..5d2932258a
--- /dev/null
+++ b/platform/default/mbgl/gl/headless_frontend.cpp
@@ -0,0 +1,86 @@
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+namespace mbgl {
+
+HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir)
+ : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir) {
+}
+
+HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir)
+ : 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, GLContextMode::Unique, programCacheDir)) {
+}
+
+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;
+}
+
+} // 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..33503bc13b
--- /dev/null
+++ b/platform/default/mbgl/gl/headless_frontend.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#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 HeadlessFrontend : public RendererFrontend {
+public:
+ HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {});
+ HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {});
+ ~HeadlessFrontend() 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&);
+
+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 5424e03c96..0000000000
--- a/platform/default/mbgl/gl/offscreen_view.cpp
+++ /dev/null
@@ -1,64 +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.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 bf1a9889cd..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();
-
- 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..95c46344fe
--- /dev/null
+++ b/platform/default/mbgl/map/map_snapshotter.cpp
@@ -0,0 +1,76 @@
+#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/storage/file_source.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/event.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 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::Still) {
+
+ map.getStyle().loadURL(styleURL);
+
+ map.jumpTo(cameraOptions);
+
+ // Set region, if specified
+ if (region) {
+ mbgl::EdgeInsets insets = { 0, 0, 0, 0 };
+ std::vector<LatLng> latLngs = { region->southwest(), region->northeast() };
+ map.jumpTo(map.cameraForLatLngs(latLngs, insets));
+ }
+}
+
+void MapSnapshotter::Impl::snapshot(ActorRef<MapSnapshotter::Callback> callback) {
+ map.renderStill([this, callback = std::move(callback)] (std::exception_ptr error) mutable {
+ callback.invoke(&MapSnapshotter::Callback::operator(), error, error ? PremultipliedImage() : frontend.readStillImage());
+ });
+}
+
+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));
+}
+
+} // 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..0afa848fd8
--- /dev/null
+++ b/platform/default/mbgl/map/map_snapshotter.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <exception>
+#include <string>
+#include <functional>
+
+namespace mbgl {
+
+template<class> class ActorRef;
+struct CameraOptions;
+class FileSource;
+class Size;
+class LatLngBounds;
+
+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();
+
+ using Callback = std::function<void (std::exception_ptr, PremultipliedImage)>;
+ void snapshot(ActorRef<Callback>);
+
+private:
+ class Impl;
+ std::unique_ptr<util::Thread<Impl>> impl;
+};
+
+} // namespace mbgl
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp
index fd2d47819b..9ec789f725 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>
@@ -24,17 +25,11 @@ 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());
+ 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(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(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 204ee04c92..65c2097182 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -49,7 +49,8 @@ void OfflineDatabase::ensureSchema() {
case 2: migrateToVersion3(); // fall through
case 3: // no-op and fall through
case 4: migrateToVersion5(); // fall through
- case 5: return;
+ case 5: migrateToVersion6(); // fall through
+ case 6: return;
default: break; // downgrade, delete the database
}
@@ -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 901f996a4f..ff61114888 100644
--- a/platform/default/mbgl/storage/offline_download.cpp
+++ b/platform/default/mbgl/storage/offline_download.cpp
@@ -5,8 +5,10 @@
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/style/parser.hpp>
-#include <mbgl/style/sources/geojson_source_impl.hpp>
-#include <mbgl/style/tile_source_impl.hpp>
+#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/image_source.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/tileset.hpp>
#include <mbgl/text/glyph.hpp>
@@ -19,6 +21,8 @@
namespace mbgl {
+using namespace style;
+
OfflineDownload::OfflineDownload(int64_t id_,
OfflineRegionDefinition&& definition_,
OfflineDatabase& offlineDatabase_,
@@ -71,40 +75,53 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
result.requiredResourceCountIsPrecise = true;
for (const auto& source : parser.sources) {
- SourceType type = source->baseImpl->type;
-
- switch (type) {
- case SourceType::Vector:
- case SourceType::Raster: {
- style::TileSourceImpl* tileSource =
- static_cast<style::TileSourceImpl*>(source->baseImpl.get());
- const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset();
- const uint16_t tileSize = tileSource->getTileSize();
+ SourceType type = source->getType();
+ 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 std::string& url = urlOrTileset.get<std::string>();
+ const auto& url = urlOrTileset.get<std::string>();
optional<Response> sourceResponse = offlineDatabase.get(Resource::source(url));
if (sourceResponse) {
style::conversion::Error error;
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;
}
}
+ };
+
+ switch (type) {
+ case SourceType::Vector: {
+ const auto& vectorSource = *source->as<VectorSource>();
+ handleTiledSource(vectorSource.getURLOrTileset(), util::tileSize);
+ break;
+ }
+
+ case SourceType::Raster: {
+ const auto& rasterSource = *source->as<RasterSource>();
+ handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize());
break;
}
case SourceType::GeoJSON: {
- style::GeoJSONSource* geojsonSource = source->as<style::GeoJSONSource>();
- if (geojsonSource->getURL()) {
+ const auto& geojsonSource = *source->as<GeoJSONSource>();
+ if (geojsonSource.getURL()) {
+ result.requiredResourceCount += 1;
+ }
+ break;
+ }
+
+ case SourceType::Image: {
+ const auto& imageSource = *source->as<ImageSource>();
+ if (imageSource.getURL()) {
result.requiredResourceCount += 1;
}
break;
@@ -138,20 +155,13 @@ void OfflineDownload::activateDownload() {
parser.parse(*styleResponse.data);
for (const auto& source : parser.sources) {
- SourceType type = source->baseImpl->type;
-
- switch (type) {
- case SourceType::Vector:
- case SourceType::Raster: {
- const style::TileSourceImpl* tileSource =
- static_cast<style::TileSourceImpl*>(source->baseImpl.get());
- const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset();
- const uint16_t tileSize = tileSource->getTileSize();
+ SourceType type = source->getType();
+ auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) {
if (urlOrTileset.is<Tileset>()) {
queueTiles(type, tileSize, urlOrTileset.get<Tileset>());
} else {
- const std::string& url = urlOrTileset.get<std::string>();
+ const auto& url = urlOrTileset.get<std::string>();
status.requiredResourceCountIsPrecise = false;
status.requiredResourceCount++;
requiredSourceURLs.insert(url);
@@ -170,15 +180,34 @@ void OfflineDownload::activateDownload() {
}
});
}
+ };
+
+ switch (type) {
+ case SourceType::Vector: {
+ const auto& vectorSource = *source->as<VectorSource>();
+ handleTiledSource(vectorSource.getURLOrTileset(), util::tileSize);
+ break;
+ }
+
+ case SourceType::Raster: {
+ const auto& rasterSource = *source->as<RasterSource>();
+ handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize());
break;
}
case SourceType::GeoJSON: {
- style::GeoJSONSource::Impl* geojsonSource =
- static_cast<style::GeoJSONSource::Impl*>(source->baseImpl.get());
+ const auto& geojsonSource = *source->as<GeoJSONSource>();
+ if (geojsonSource.getURL()) {
+ queueResource(Resource::source(*geojsonSource.getURL()));
+ }
+ break;
+ }
- if (geojsonSource->getURL()) {
- queueResource(Resource::source(*geojsonSource->getURL()));
+ case SourceType::Image: {
+ const auto& imageSource = *source->as<ImageSource>();
+ auto imageUrl = imageSource.getURL();
+ if (imageUrl && !imageUrl->empty()) {
+ queueResource(Resource::image(*imageUrl));
}
break;
}
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/online_file_source.cpp b/platform/default/online_file_source.cpp
index a72b6f4efc..d685109b95 100644
--- a/platform/default/online_file_source.cpp
+++ b/platform/default/online_file_source.cpp
@@ -2,16 +2,18 @@
#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
+#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/logging.hpp>
+#include <mbgl/actor/mailbox.hpp>
#include <mbgl/util/constants.hpp>
-#include <mbgl/util/thread.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
#include <mbgl/util/http_timeout.hpp>
@@ -35,12 +37,17 @@ public:
void schedule(optional<Timestamp> expires);
void completed(Response);
+ void setTransformedURL(const std::string&& url);
+ ActorRef<OnlineFileRequest> actor();
+
OnlineFileSource::Impl& impl;
Resource resource;
std::unique_ptr<AsyncRequest> request;
util::Timer timer;
Callback callback;
+ std::shared_ptr<Mailbox> mailbox;
+
// Counts the number of times a response was already expired when received. We're using
// this to add a delay when making a new request so we don't keep retrying immediately
// in case of a server serving expired tiles.
@@ -66,15 +73,12 @@ public:
void add(OnlineFileRequest* request) {
allRequests.insert(request);
if (resourceTransform) {
- // When there's a Resource transform callback set, replace the resource with the
+ // Request the ResourceTransform actor a new url and replace the resource url with the
// transformed one before proceeding to schedule the request.
- request->request =
- resourceTransform(request->resource.kind, std::move(request->resource.url),
- [request](std::string&& url) {
- request->request.release();
- request->resource.url = std::move(url);
- request->schedule();
- });
+ resourceTransform->invoke(&ResourceTransform::transform, request->resource.kind,
+ std::move(request->resource.url), [ref = request->actor()](const std::string&& url) mutable {
+ ref.invoke(&OnlineFileRequest::setTransformedURL, std::move(url));
+ });
} else {
request->schedule();
}
@@ -113,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());
}
@@ -145,10 +160,15 @@ public:
return activeRequests.find(request) != activeRequests.end();
}
- void setResourceTransform(ResourceTransform&& transform) {
+ void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
resourceTransform = std::move(transform);
}
+ void setOnlineStatus(const bool status) {
+ online = status;
+ networkIsReachableAgain();
+ }
+
private:
void networkIsReachableAgain() {
for (auto& request : allRequests) {
@@ -156,7 +176,7 @@ private:
}
}
- ResourceTransform resourceTransform;
+ optional<ActorRef<ResourceTransform>> resourceTransform;
/**
* The lifetime of a request is:
@@ -174,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) };
};
@@ -189,6 +210,7 @@ std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource
switch (resource.kind) {
case Resource::Kind::Unknown:
+ case Resource::Kind::Image:
break;
case Resource::Kind::Style:
@@ -216,7 +238,7 @@ std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource
return std::make_unique<OnlineFileRequest>(std::move(res), std::move(callback), *impl);
}
-void OnlineFileSource::setResourceTransform(ResourceTransform&& transform) {
+void OnlineFileSource::setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
impl->setResourceTransform(std::move(transform));
}
@@ -315,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) {
@@ -361,4 +391,25 @@ void OnlineFileRequest::networkIsReachableAgain() {
}
}
+void OnlineFileRequest::setTransformedURL(const std::string&& url) {
+ resource.url = std::move(url);
+ schedule();
+}
+
+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>(*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/png_reader.cpp b/platform/default/png_reader.cpp
index 29ef3058e1..4d4ee29d1f 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -40,7 +40,7 @@ static void user_warning_fn(png_structp, png_const_charp warning_msg) {
}
static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
- std::istream* fin = reinterpret_cast<std::istream*>(png_get_io_ptr(png_ptr));
+ auto* fin = reinterpret_cast<std::istream*>(png_get_io_ptr(png_ptr));
fin->read(reinterpret_cast<char*>(data), length);
std::streamsize read_count = fin->gcount();
if (read_count < 0 || static_cast<png_size_t>(read_count) != length)
diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp
index 98d1badcb5..6375dba78e 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,7 +126,7 @@ RunLoop::~RunLoop() {
}
LOOP_HANDLE RunLoop::getLoopHandle() {
- return current.get()->impl->loop;
+ return Get()->impl->loop;
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp
index 304d5e9aba..2e08354fdf 100644
--- a/platform/default/sqlite3.cpp
+++ b/platform/default/sqlite3.cpp
@@ -107,8 +107,7 @@ Database &Database::operator=(Database &&other) {
return *this;
}
-Database::~Database() {
-}
+Database::~Database() = default;
void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
assert(impl);
@@ -151,8 +150,7 @@ Statement &Statement::operator=(Statement &&other) {
return *this;
}
-Statement::~Statement() {
-}
+Statement::~Statement() = default;
template <> void Statement::bind(int offset, std::nullptr_t) {
assert(impl);
@@ -289,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);
@@ -314,7 +317,7 @@ template <> std::string Statement::get(int offset) {
template <> std::vector<uint8_t> Statement::get(int offset) {
assert(impl);
- const uint8_t* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(impl->stmt, offset));
+ const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(impl->stmt, offset));
const uint8_t* end = begin + sqlite3_column_bytes(impl->stmt, offset);
return { begin, end };
}
@@ -383,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/timer.cpp b/platform/default/timer.cpp
index cd0e6da6aa..90a85bfc1f 100644
--- a/platform/default/timer.cpp
+++ b/platform/default/timer.cpp
@@ -10,7 +10,7 @@ namespace util {
class Timer::Impl {
public:
Impl() : timer(new uv_timer_t) {
- uv_loop_t* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle());
+ auto* loop = reinterpret_cast<uv_loop_t*>(RunLoop::getLoopHandle());
if (uv_timer_init(loop, timer) != 0) {
throw std::runtime_error("Failed to initialize timer.");
}
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 31b0b92c58..d591f897e0 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -1,15 +1,24 @@
#include "glfw_view.hpp"
+#include "glfw_renderer_frontend.hpp"
+#include "ny_route.hpp"
#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/util/logging.hpp>
#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>
+#include <mapbox/geometry.hpp>
+#include <mapbox/geojson.hpp>
+
#if MBGL_USE_GLES2
#define GLFW_INCLUDE_ES2
#endif // MBGL_USE_GLES2
@@ -99,7 +108,9 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
printf("- Press `S` to cycle through bundled styles\n");
printf("- Press `X` to reset the transform\n");
printf("- Press `N` to reset north\n");
- printf("- Press `R` to toggle any available `night` style class\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");
@@ -131,23 +142,30 @@ GLFWView::~GLFWView() {
void GLFWView::setMap(mbgl::Map *map_) {
map = map_;
- map->addAnnotationImage("default_marker", makeImage(22, 22, 1));
+ map->addAnnotationImage(makeImage("default_marker", 22, 22, 1));
+}
+
+void GLFWView::setRenderFrontend(GLFWRendererFrontend* rendererFrontend_) {
+ rendererFrontend = rendererFrontend_;
}
void GLFWView::updateAssumedState() {
assumeFramebufferBinding(0);
- assumeViewportSize(getFramebufferSize());
+ assumeViewport(0, 0, getFramebufferSize());
}
void GLFWView::bind() {
setFramebufferBinding(0);
- setViewportSize(getFramebufferSize());
+ setViewport(0, 0, getFramebufferSize());
}
void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) {
- GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
+ auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
if (action == GLFW_RELEASE) {
+ if (key != GLFW_KEY_R || key != GLFW_KEY_S)
+ view->animateRouteCallback = nullptr;
+
switch (key) {
case GLFW_KEY_ESCAPE:
glfwSetWindowShouldClose(window, true);
@@ -159,21 +177,13 @@ 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();
break;
- case GLFW_KEY_R:
- if (!mods) {
- static const mbgl::style::TransitionOptions transition { { mbgl::Milliseconds(300) } };
- view->map->setTransitionOptions(transition);
- if (view->map->hasClass("night")) {
- view->map->removeClass("night");
- } else {
- view->map->addClass("night");
- }
- }
- break;
#if not MBGL_USE_GLES2
case GLFW_KEY_B: {
auto debug = view->map->getDebug();
@@ -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:
@@ -231,6 +241,37 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
view->map->flyTo(cameraOptions, animationOptions);
nextPlace = nextPlace % places.size();
} break;
+ case GLFW_KEY_R: {
+ view->show3DExtrusions = true;
+ view->toggle3DExtrusions(view->show3DExtrusions);
+ if (view->animateRouteCallback) break;
+ view->animateRouteCallback = [](mbgl::Map* routeMap) {
+ static mapbox::cheap_ruler::CheapRuler ruler { 40.7 }; // New York
+ static mapbox::geojson::geojson route { mapbox::geojson::parse(mbgl::platform::glfw::route) };
+ const auto& geometry = route.get<mapbox::geometry::geometry<double>>();
+ const auto& lineString = geometry.get<mapbox::geometry::line_string<double>>();
+
+ static double routeDistance = ruler.lineDistance(lineString);
+ static double routeProgress = 0;
+ routeProgress += 0.0005;
+ if (routeProgress > 1.0) routeProgress = 0;
+
+ double distance = routeProgress * routeDistance;
+ auto point = ruler.along(lineString, distance);
+ auto latLng = routeMap->getLatLng();
+ routeMap->setLatLng({ point.y, point.x });
+ double bearing = ruler.bearing({ latLng.longitude(), latLng.latitude() }, point);
+ double easing = bearing - routeMap->getBearing();
+ easing += easing > 180.0 ? -360.0 : easing < -180 ? 360.0 : 0;
+ routeMap->setBearing(routeMap->getBearing() + (easing / 20));
+ routeMap->setPitch(60.0);
+ routeMap->setZoom(18.0);
+ };
+ view->animateRouteCallback(view->map);
+ } break;
+ case GLFW_KEY_E:
+ view->toggle3DExtrusions(!view->show3DExtrusions);
+ break;
}
}
@@ -266,7 +307,7 @@ mbgl::Point<double> GLFWView::makeRandomPoint() const {
}
std::unique_ptr<mbgl::style::Image>
-GLFWView::makeImage(int width, int height, float pixelRatio) {
+GLFWView::makeImage(const std::string& id, int width, int height, float pixelRatio) {
const int r = 255 * (double(std::rand()) / RAND_MAX);
const int g = 255 * (double(std::rand()) / RAND_MAX);
const int b = 255 * (double(std::rand()) / RAND_MAX);
@@ -291,7 +332,7 @@ GLFWView::makeImage(int width, int height, float pixelRatio) {
}
}
- return std::make_unique<mbgl::style::Image>(std::move(image), pixelRatio);
+ return std::make_unique<mbgl::style::Image>(id, std::move(image), pixelRatio);
}
void GLFWView::nextOrientation() {
@@ -308,7 +349,7 @@ void GLFWView::addRandomCustomPointAnnotations(int count) {
for (int i = 0; i < count; i++) {
static int spriteID = 1;
const auto name = std::string{ "marker-" } + mbgl::util::toString(spriteID++);
- map->addAnnotationImage(name, makeImage(22, 22, 1));
+ map->addAnnotationImage(makeImage(name, 22, 22, 1));
spriteIDs.push_back(name);
annotationIDs.push_back(map->addAnnotation(mbgl::SymbolAnnotation { makeRandomPoint(), name }));
}
@@ -356,7 +397,7 @@ void GLFWView::popAnnotation() {
}
void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset) {
- GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
+ auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
double delta = yOffset * 40;
bool isWheel = delta != 0 && std::fmod(delta, 4.000244140625) == 0;
@@ -378,16 +419,18 @@ void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset)
}
void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) {
- GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
+ auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
view->width = width;
view->height = height;
view->map->setSize({ static_cast<uint32_t>(view->width), static_cast<uint32_t>(view->height) });
}
void GLFWView::onFramebufferResize(GLFWwindow *window, int width, int height) {
- GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
+ auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
view->fbWidth = width;
view->fbHeight = height;
+
+ mbgl::BackendScope scope { *view, mbgl::BackendScope::ScopeType::Implicit };
view->bind();
// This is only triggered when the framebuffer is resized, but not the window. It can
@@ -398,7 +441,7 @@ void GLFWView::onFramebufferResize(GLFWwindow *window, int width, int height) {
}
void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modifiers) {
- GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
+ auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
if (button == GLFW_MOUSE_BUTTON_RIGHT ||
(button == GLFW_MOUSE_BUTTON_LEFT && modifiers & GLFW_MOD_CONTROL)) {
@@ -426,7 +469,7 @@ void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modi
}
void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) {
- GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
+ auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
if (view->tracking) {
double dx = x - view->lastX;
double dy = y - view->lastY;
@@ -456,13 +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);
@@ -471,7 +517,6 @@ void GLFWView::run() {
invalidate();
}
- dirty = false;
}
};
@@ -540,6 +585,51 @@ void GLFWView::setWindowTitle(const std::string& title) {
glfwSetWindowTitle(window, (std::string { "Mapbox GL: " } + title).c_str());
}
+void GLFWView::onDidFinishLoadingStyle() {
+ if (show3DExtrusions) {
+ toggle3DExtrusions(show3DExtrusions);
+ }
+}
+
+void GLFWView::toggle3DExtrusions(bool visible) {
+ show3DExtrusions = visible;
+
+ // Satellite-only style does not contain building extrusions data.
+ if (!map->getStyle().getSource("composite")) {
+ return;
+ }
+
+ if (auto layer = map->getStyle().getLayer("3d-buildings")) {
+ layer->setVisibility(mbgl::style::VisibilityType(!show3DExtrusions));
+ return;
+ }
+
+ auto extrusionLayer = std::make_unique<mbgl::style::FillExtrusionLayer>("3d-buildings", "composite");
+ extrusionLayer->setSourceLayer("building");
+ extrusionLayer->setMinZoom(15.0f);
+ extrusionLayer->setFilter(mbgl::style::EqualsFilter { "extrude", { std::string("true") } });
+
+ auto colorFn = mbgl::style::SourceFunction<mbgl::Color> { "height",
+ mbgl::style::ExponentialStops<mbgl::Color> {
+ std::map<float, mbgl::Color> {
+ { 0.f, *mbgl::Color::parse("#160e23") },
+ { 50.f, *mbgl::Color::parse("#00615f") },
+ { 100.f, *mbgl::Color::parse("#55e9ff") }
+ }
+ }
+ };
+ extrusionLayer->setFillExtrusionColor({ colorFn });
+ extrusionLayer->setFillExtrusionOpacity({ 0.6f });
+
+ auto heightSourceFn = mbgl::style::SourceFunction<float> { "height", mbgl::style::IdentityStops<float>() };
+ extrusionLayer->setFillExtrusionHeight({ heightSourceFn });
+
+ auto baseSourceFn = mbgl::style::SourceFunction<float> { "min_height", mbgl::style::IdentityStops<float>() };
+ extrusionLayer->setFillExtrusionBase({ baseSourceFn });
+
+ map->getStyle().addLayer(std::move(extrusionLayer));
+}
+
namespace mbgl {
namespace platform {
diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp
index 77f4f64b01..d3257f6b9a 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,21 +28,28 @@ 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
+ void onDidFinishLoadingStyle() override;
+
protected:
// mbgl::Backend implementation
mbgl::gl::ProcAddress initializeExtension(const char*) override;
@@ -61,7 +70,7 @@ private:
mbgl::Color makeRandomColor() const;
mbgl::Point<double> makeRandomPoint() const;
- static std::unique_ptr<mbgl::style::Image> makeImage(int width, int height, float pixelRatio);
+ static std::unique_ptr<mbgl::style::Image> makeImage(const std::string& id, int width, int height, float pixelRatio);
void nextOrientation();
@@ -77,13 +86,17 @@ private:
std::vector<std::string> spriteIDs;
private:
+ void toggle3DExtrusions(bool visible);
+
mbgl::Map* map = nullptr;
+ GLFWRendererFrontend* rendererFrontend = nullptr;
bool fullscreen = false;
const bool benchmark = false;
bool tracking = false;
bool rotating = false;
bool pitching = false;
+ bool show3DExtrusions = false;
// Frame timer
int frames = 0;
@@ -102,6 +115,8 @@ private:
std::function<void()> changeStyleCallback;
std::function<void()> pauseResumeCallback;
+ std::function<void()> onlineStatusCallback;
+ std::function<void(mbgl::Map*)> animateRouteCallback;
mbgl::util::RunLoop runLoop;
mbgl::util::Timer frameTick;
diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp
index 59d2ce3ec6..dadd88c705 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>
@@ -6,8 +7,10 @@
#include <mbgl/util/platform.hpp>
#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 <signal.h>
+#include <csignal>
#include <getopt.h>
#include <fstream>
#include <sstream>
@@ -19,7 +22,7 @@ namespace {
GLFWView* view = nullptr;
-}
+} // namespace
void quit_handler(int) {
if (view) {
@@ -31,23 +34,24 @@ void quit_handler(int) {
}
int main(int argc, char *argv[]) {
+ // Load settings
+ mbgl::Settings_JSON settings;
+
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, 0, 'f'},
- {"benchmark", no_argument, 0, 'b'},
- {"style", required_argument, 0, 's'},
- {"lon", required_argument, 0, 'x'},
- {"lat", required_argument, 0, 'y'},
- {"zoom", required_argument, 0, 'z'},
- {"bearing", required_argument, 0, 'r'},
- {"pitch", required_argument, 0, 'p'},
- {0, 0, 0, 0}
+ {"fullscreen", no_argument, nullptr, 'f'},
+ {"benchmark", no_argument, nullptr, 'b'},
+ {"offline", no_argument, nullptr, 'o'},
+ {"style", required_argument, nullptr, 's'},
+ {"lon", required_argument, nullptr, 'x'},
+ {"lat", required_argument, nullptr, 'y'},
+ {"zoom", required_argument, nullptr, 'z'},
+ {"bearing", required_argument, nullptr, 'r'},
+ {"pitch", required_argument, nullptr, 'p'},
+ {nullptr, 0, nullptr, 0}
};
while (true) {
@@ -56,37 +60,32 @@ int main(int argc, char *argv[]) {
if (opt == -1) break;
switch (opt)
{
- case 0:
- if (long_options[option_index].flag != 0)
- break;
case 'f':
fullscreen = true;
break;
case 'b':
benchmark = true;
break;
+ case 'o':
+ settings.online = false;
+ break;
case 's':
- style = std::string("asset://") + std::string(optarg);
+ style = std::string(optarg);
break;
case 'x':
- longitude = atof(optarg);
- skipConfig = true;
+ settings.longitude = atof(optarg);
break;
case 'y':
- latitude = atof(optarg);
- skipConfig = true;
+ settings.latitude = atof(optarg);
break;
case 'z':
- zoom = atof(optarg);
- skipConfig = true;
+ settings.zoom = atof(optarg);
break;
case 'r':
- bearing = atof(optarg);
- skipConfig = true;
+ settings.bearing = atof(optarg);
break;
case 'p':
- pitch = atof(optarg);
- skipConfig = true;
+ settings.pitch = atof(optarg);
break;
default:
break;
@@ -99,7 +98,7 @@ int main(int argc, char *argv[]) {
sigIntHandler.sa_handler = quit_handler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
- sigaction(SIGINT, &sigIntHandler, NULL);
+ sigaction(SIGINT, &sigIntHandler, nullptr);
if (benchmark) {
mbgl::Log::Info(mbgl::Event::General, "BENCHMARK MODE: Some optimizations are disabled.");
@@ -109,6 +108,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");
@@ -119,26 +122,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;
@@ -147,7 +150,7 @@ int main(int argc, char *argv[]) {
}
mbgl::util::default_styles::DefaultStyle newStyle = mbgl::util::default_styles::orderedStyles[currentStyleIndex];
- map.setStyleURL(newStyle.url);
+ map.getStyle().loadURL(newStyle.url);
view->setWindowTitle(newStyle.name);
mbgl::Log::Info(mbgl::Event::Setup, "Changed style to: %s", newStyle.name);
@@ -178,7 +181,7 @@ int main(int argc, char *argv[]) {
}
}
- map.setStyleURL(style);
+ map.getStyle().loadURL(style);
view->run();
@@ -190,13 +193,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,
- "Exit location: --lat=\"%f\" --lon=\"%f\" --zoom=\"%f\" --bearing \"%f\"",
+ 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/ny_route.hpp b/platform/glfw/ny_route.hpp
new file mode 100644
index 0000000000..c3d5157106
--- /dev/null
+++ b/platform/glfw/ny_route.hpp
@@ -0,0 +1,104 @@
+#include <string>
+
+namespace mbgl {
+namespace platform {
+namespace glfw {
+
+constexpr const char* route = R"route(
+{
+ "coordinates": [
+ [ -74.013841, 40.702449 ],
+ [ -74.013863, 40.702462 ],
+ [ -74.013977, 40.702548 ],
+ [ -74.01404, 40.702595 ],
+ [ -74.014152, 40.702685 ],
+ [ -74.014213, 40.702749 ],
+ [ -74.014284, 40.702835 ],
+ [ -74.014333, 40.702911 ],
+ [ -74.014368, 40.702978 ],
+ [ -74.014407, 40.703066 ],
+ [ -74.014438, 40.703152 ],
+ [ -74.014449, 40.703209 ],
+ [ -74.01445, 40.703263 ],
+ [ -74.01445, 40.703332 ],
+ [ -74.014442, 40.703401 ],
+ [ -74.014404, 40.703614 ],
+ [ -74.014245, 40.704524 ],
+ [ -74.01422, 40.704633 ],
+ [ -74.014329, 40.704667 ],
+ [ -74.01445, 40.704705 ],
+ [ -74.014548, 40.704733 ],
+ [ -74.014641, 40.704756 ],
+ [ -74.014727, 40.704776 ],
+ [ -74.014841, 40.704799 ],
+ [ -74.014977, 40.704827 ],
+ [ -74.015033, 40.704838 ],
+ [ -74.015365, 40.704905 ],
+ [ -74.015454, 40.704921 ],
+ [ -74.015541, 40.704933 ],
+ [ -74.015638, 40.704945 ],
+ [ -74.015699, 40.70495 ],
+ [ -74.015755, 40.704953 ],
+ [ -74.01583, 40.704952 ],
+ [ -74.015909, 40.704949 ],
+ [ -74.016073, 40.704935 ],
+ [ -74.016157, 40.704927 ],
+ [ -74.016224, 40.704921 ],
+ [ -74.016284, 40.70491 ],
+ [ -74.016416, 40.704882 ],
+ [ -74.016424, 40.704918 ],
+ [ -74.016437, 40.704962 ],
+ [ -74.016453, 40.705007 ],
+ [ -74.016462, 40.705041 ],
+ [ -74.016467, 40.705072 ],
+ [ -74.016463, 40.705112 ],
+ [ -74.016457, 40.70515 ],
+ [ -74.016447, 40.705189 ],
+ [ -74.016151, 40.705949 ],
+ [ -74.016121, 40.706032 ],
+ [ -74.01609, 40.706121 ],
+ [ -74.01606, 40.706214 ],
+ [ -74.016037, 40.706296 ],
+ [ -74.016016, 40.706383 ],
+ [ -74.016003, 40.70645 ],
+ [ -74.015986, 40.706549 ],
+ [ -74.015971, 40.706613 ],
+ [ -74.015953, 40.706677 ],
+ [ -74.015888, 40.706844 ],
+ [ -74.015805, 40.707053 ],
+ [ -74.015735, 40.707222 ],
+ [ -74.015697, 40.707307 ],
+ [ -74.015597, 40.70752 ],
+ [ -74.015512, 40.707701 ],
+ [ -74.015476, 40.707784 ],
+ [ -74.015442, 40.707859 ],
+ [ -74.015363, 40.708065 ],
+ [ -74.015197, 40.708495 ],
+ [ -74.014864, 40.709446 ],
+ [ -74.01476, 40.709725 ],
+ [ -74.014744, 40.709777 ],
+ [ -74.014729, 40.709827 ],
+ [ -74.01472, 40.709873 ],
+ [ -74.014712, 40.709925 ],
+ [ -74.014709, 40.709998 ],
+ [ -74.014699, 40.710139 ],
+ [ -74.014689, 40.710215 ],
+ [ -74.014674, 40.710286 ],
+ [ -74.014655, 40.710373 ],
+ [ -74.014631, 40.710477 ],
+ [ -74.014602, 40.710583 ],
+ [ -74.014523, 40.710825 ],
+ [ -74.014492, 40.710899 ],
+ [ -74.014463, 40.710966 ],
+ [ -74.014434, 40.711033 ],
+ [ -74.014406, 40.711098 ],
+ [ -74.01438, 40.711171 ],
+ [ -74.01436, 40.71125 ],
+ [ -74.014147, 40.712245 ]
+ ],
+ "type": "LineString"
+})route";
+
+} // namespace glfw
+} // namespace platform
+} // namespace mbgl
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..49ea00e3e1 100644
--- a/platform/glfw/settings_json.hpp
+++ b/platform/glfw/settings_json.hpp
@@ -19,6 +19,7 @@ public:
double pitch = 0;
EnumType debug = 0;
+ bool online = true;
};
} // namespace mbgl
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index fa52be414e..8dd1c5d8c7 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -2,11 +2,43 @@
Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
-## 3.6.4
+## master
+
+### 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 and user interaction
+
+* 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))
+* 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))
+
+### Other changes
+
+* 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))
+
+## 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
+## 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))
diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md
index bcb837c243..43d7ef3c75 100644
--- a/platform/ios/DEVELOPING.md
+++ b/platform/ios/DEVELOPING.md
@@ -151,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.
diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
index aec2b8e350..1abcaf467c 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.4'
+ version = '3.7.0-alpha.1'
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 89bf3d417a..ceb7f21c47 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.4'
+ version = '3.7.0-alpha.1'
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 9f694d8e58..bcd37d0d06 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.4'
+ version = '3.7.0-alpha.1'
m.name = 'Mapbox-iOS-SDK'
m.version = version
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..d26479f085
--- /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* snapshotterTL;
+ MGLMapSnapshotter* snapshotterTM;
+ MGLMapSnapshotter* snapshotterTR;
+
+ // Bottom row
+ MGLMapSnapshotter* snapshotterBL;
+ MGLMapSnapshotter* snapshotterBM;
+ MGLMapSnapshotter* snapshotterBR;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Start snapshotters
+ snapshotterTL = [self startSnapshotterForImageView:_snapshotImageViewTL coordinates:CLLocationCoordinate2DMake(37.7184, -122.4365)];
+ snapshotterTM = [self startSnapshotterForImageView:_snapshotImageViewTM coordinates:CLLocationCoordinate2DMake(38.8936, -77.0146)];
+ snapshotterTR = [self startSnapshotterForImageView:_snapshotImageViewTR coordinates:CLLocationCoordinate2DMake(-13.1356, -74.2442)];
+
+ snapshotterBL = [self startSnapshotterForImageView:_snapshotImageViewBL coordinates:CLLocationCoordinate2DMake(52.5072, 13.4247)];
+ snapshotterBM = [self startSnapshotterForImageView:_snapshotImageViewBM coordinates:CLLocationCoordinate2DMake(60.2118, 24.6754)];
+ snapshotterBR = [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:[NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"] camera:mapCamera size:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];
+ options.zoom = 10;
+
+ // Create and start the snapshotter
+ MGLMapSnapshotter* snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options];
+ [snapshotter startWithCompletionHandler: ^(UIImage *image, NSError *error) {
+ if (error) {
+ NSLog(@"Could not load snapshot: %@", [error localizedDescription]);
+ } else {
+ imageView.image = image;
+ }
+ }];
+
+ return snapshotter;
+}
+
+
+@end
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index cdfdb4efe1..07838bc6bd 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -52,7 +52,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
};
typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
- MBXSettingsRuntimeStylingWater = 0,
+ MBXSettingsRuntimeStylingBuildingExtrusions = 0,
+ MBXSettingsRuntimeStylingWater,
MBXSettingsRuntimeStylingRoads,
MBXSettingsRuntimeStylingRaster,
MBXSettingsRuntimeStylingShape,
@@ -71,6 +72,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
MBXSettingsRuntimeStylingUpdateShapeSourceFeatures,
MBXSettingsRuntimeStylingVectorSource,
MBXSettingsRuntimeStylingRasterSource,
+ MBXSettingsRuntimeStylingImageSource,
MBXSettingsRuntimeStylingRouteLine,
MBXSettingsRuntimeStylingDDSPolygon,
};
@@ -82,6 +84,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MBXSettingsMiscellaneousScrollView,
MBXSettingsMiscellaneousToggleTwoMaps,
MBXSettingsMiscellaneousCountryLabels,
+ MBXSettingsMiscellaneousShowSnapshots,
MBXSettingsMiscellaneousPrintLogFile,
MBXSettingsMiscellaneousDeleteLogFile,
};
@@ -325,6 +328,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
break;
case MBXSettingsRuntimeStyling:
[settingsTitles addObjectsFromArray:@[
+ @"Add Building Extrusions",
@"Style Water With Function",
@"Style Roads With Function",
@"Add Raster & Apply Function",
@@ -344,6 +348,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Update Shape Source: Features",
@"Style Vector Source",
@"Style Raster Source",
+ @"Style Image Source",
@"Add Route Line",
@"Dynamically Style Polygon",
]];
@@ -356,6 +361,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"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)
@@ -452,6 +458,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsRuntimeStyling:
switch (indexPath.row)
{
+ case MBXSettingsRuntimeStylingBuildingExtrusions:
+ [self styleBuildingExtrusions];
+ break;
case MBXSettingsRuntimeStylingWater:
[self styleWaterLayer];
break;
@@ -509,6 +518,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsRuntimeStylingRasterSource:
[self styleRasterSource];
break;
+ case MBXSettingsRuntimeStylingImageSource:
+ [self styleImageSource];
+ break;
case MBXSettingsRuntimeStylingRouteLine:
[self styleRouteLine];
break;
@@ -633,6 +645,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
constant:0]];
}
break;
+ case MBXSettingsMiscellaneousShowSnapshots:
+ {
+ [self performSegueWithIdentifier:@"ShowSnapshots" sender:nil];
+ break;
+ }
default:
NSAssert(NO, @"All miscellaneous setting rows should be implemented");
break;
@@ -863,6 +880,38 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView showAnnotations:annotations animated:YES];
}
+- (void)styleBuildingExtrusions
+{
+ MGLSource* source = [self.mapView.style sourceWithIdentifier:@"composite"];
+ if (source) {
+
+ MGLFillExtrusionStyleLayer* layer = [[MGLFillExtrusionStyleLayer alloc] initWithIdentifier:@"extrudedBuildings" source:source];
+ layer.sourceLayerIdentifier = @"building";
+ layer.predicate = [NSPredicate predicateWithFormat:@"extrude == 'true' AND height > 0"];
+ layer.fillExtrusionBase = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"min_height" options:nil];
+ layer.fillExtrusionHeight = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"height" options:nil];
+
+ // Set the fill color to that of the existing building footprint layer, if it exists.
+ MGLFillStyleLayer* buildingLayer = (MGLFillStyleLayer*)[self.mapView.style layerWithIdentifier:@"building"];
+ if (buildingLayer) {
+ if (buildingLayer.fillColor) {
+ layer.fillExtrusionColor = buildingLayer.fillColor;
+ } else {
+ layer.fillExtrusionColor = [MGLStyleValue valueWithRawValue:[UIColor whiteColor]];
+ }
+
+ layer.fillExtrusionOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.75];
+ }
+
+ MGLStyleLayer* labelLayer = [self.mapView.style layerWithIdentifier:@"waterway-label"];
+ if (labelLayer) {
+ [self.mapView.style insertLayer:layer belowLayer:labelLayer];
+ } else {
+ [self.mapView.style addLayer:layer];
+ }
+ }
+}
+
- (void)styleWaterLayer
{
MGLFillStyleLayer *waterLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"water"];
@@ -1274,6 +1323,39 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addLayer:rasterLayer];
}
+- (void)styleImageSource
+{
+ MGLCoordinateQuad coordinateQuad = {
+ { 46.437, -80.425 },
+ { 37.936, -80.425 },
+ { 37.936, -71.516 },
+ { 46.437, -71.516 } };
+
+ MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"style-image-source-id" coordinateQuad:coordinateQuad URL:[NSURL URLWithString:@"https://www.mapbox.com/mapbox-gl-js/assets/radar0.gif"]];
+
+ [self.mapView.style addSource:imageSource];
+
+ MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"style-raster-image-layer-id" source:imageSource];
+ [self.mapView.style addLayer:rasterLayer];
+
+ [NSTimer scheduledTimerWithTimeInterval:1.0
+ target:self
+ selector:@selector(updateAnimatedImageSource:)
+ userInfo:imageSource
+ repeats:YES];
+}
+
+
+- (void)updateAnimatedImageSource:(NSTimer *)timer {
+ static int radarSuffix = 0;
+ MGLImageSource *imageSource = (MGLImageSource *)timer.userInfo;
+ NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://www.mapbox.com/mapbox-gl-js/assets/radar%d.gif", radarSuffix++]];
+ [imageSource setValue:url forKey:@"URL"];
+ if (radarSuffix > 3) {
+ radarSuffix = 0;
+ }
+}
+
-(void)styleCountryLabelsLanguage
{
_usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard
index 4bbefd7d22..507582213f 100644
--- a/platform/ios/app/Main.storyboard
+++ b/platform/ios/app/Main.storyboard
@@ -104,6 +104,7 @@
<connections>
<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"/>
@@ -360,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/bitrise.yml b/platform/ios/bitrise.yml
index 53287ec06d..36adbf7a77 100644
--- a/platform/ios/bitrise.yml
+++ b/platform/ios/bitrise.yml
@@ -17,8 +17,6 @@ workflows:
#!/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
diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake
index 836d0678cb..4e873e73ea 100644
--- a/platform/ios/config.cmake
+++ b/platform/ios/config.cmake
@@ -40,6 +40,7 @@ macro(mbgl_platform_core)
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
@@ -48,13 +49,17 @@ 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
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 ab1702a076..2698d5564f 100644
--- a/platform/ios/docs/guides/Adding Points to a Map.md
+++ b/platform/ios/docs/guides/Adding Points to a Map.md
@@ -36,7 +36,6 @@ By default, annotations added to the map are displayed with a red pin ([example]
* Annotation images are purely static and cannot be animated
* No control over z-ordering
-* Limits to the number and size of images you can add
### Annotation Views (`MGLAnnotationView`)
diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md
index 35375ea2a9..7eabfed777 100644
--- a/platform/ios/docs/guides/For Style Authors.md
+++ b/platform/ios/docs/guides/For Style Authors.md
@@ -109,7 +109,6 @@ the following terms for concepts defined in the style specification:
In the style specification | In the SDK
---------------------------|---------
-class | style class
filter | predicate
function type | interpolation mode
id | identifier
@@ -130,8 +129,9 @@ In style JSON | In the SDK
`geojson` | `MGLShapeSource`
`raster` | `MGLRasterSource`
`vector` | `MGLVectorSource`
+`image` | `MGLImageSource`
-`canvas`, `image`, and `video` sources are not supported.
+`canvas` and `video` sources are not supported.
### Tile sources
@@ -172,6 +172,12 @@ To create a shape source from local GeoJSON data, first
[convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects),
then use the `-[MGLShapeSource initWithIdentifier:shape:options:]` method.
+### Image sources
+
+Image sources accept a non-axis aligned quadrilateral as their geographic coordinates.
+These coordinates, in `MGLCoordinateQuad`, are described in counterclockwise order,
+in contrast to the clockwise order defined in the style specification.
+
## Configuring the map content’s appearance
Each layer defined by the style JSON file is represented at runtime by a style
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 22c9b43f0f..debbf1ccf3 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -7,6 +7,11 @@
objects = {
/* Begin PBXBuildFile section */
+ 071BBAFF1EE7613E001FB02A /* MGLImageSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */; };
+ 071BBB001EE7613F001FB02A /* MGLImageSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */; };
+ 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 */; };
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, ); }; };
@@ -148,7 +153,6 @@
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, ); }; };
@@ -191,6 +195,8 @@
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, ); }; };
556660D81E1D085500E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D71E1D085500E2C41B /* MGLVersionNumber.m */; };
556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -210,6 +216,13 @@
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 */; };
@@ -551,6 +564,9 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 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>"; };
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>"; };
@@ -685,6 +701,11 @@
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>"; };
@@ -962,6 +983,7 @@
buildActionMask = 2147483647;
files = (
DA8847D91CBAF91600AB86E3 /* Mapbox.framework in Frameworks */,
+ 5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -996,6 +1018,7 @@
buildActionMask = 2147483647;
files = (
DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */,
+ 5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1005,6 +1028,8 @@
35136D491D4277EA00C20EFD /* Sources */ = {
isa = PBXGroup;
children = (
+ 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */,
+ 071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */,
3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */,
DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */,
3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */,
@@ -1162,6 +1187,7 @@
40CFA64E1D78754A008103BD /* Sources */ = {
isa = PBXGroup;
children = (
+ 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */,
40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */,
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */,
4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */,
@@ -1224,6 +1250,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 */,
@@ -1347,8 +1375,11 @@
558DE79F1E5615E400C7916D /* MGLFoundation.mm */,
DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */,
DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */,
+ 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */,
+ 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */,
DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */,
DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */,
+ 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */,
DA8847EC1CBAFA5100AB86E3 /* MGLStyle.h */,
35E0CFE51D3E501500188327 /* MGLStyle_Private.h */,
DA88480F1CBAFA6200AB86E3 /* MGLStyle.mm */,
@@ -1712,7 +1743,9 @@
35D3A1E61E9BE7EB002B38EE /* MGLScaleBar.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 */,
@@ -1729,6 +1762,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 */,
@@ -1798,12 +1832,14 @@
3566C7671D4A77BA008152BC /* MGLShapeSource.h in Headers */,
DA35A29F1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
404C26E31D89B877000AA13D /* MGLTileSource.h in Headers */,
+ 071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */,
DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */,
3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
35D3A1E71E9BE7EC002B38EE /* MGLScaleBar.h in Headers */,
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 */,
@@ -2009,7 +2045,6 @@
TargetAttributes = {
DA1DC9491CB6C1C2006E619F = {
CreatedOnToolsVersion = 7.3;
- DevelopmentTeam = GJZR2MEM28;
LastSwiftMigration = 0820;
};
DA25D5B81CCD9EDE00607828 = {
@@ -2174,6 +2209,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 */,
@@ -2194,6 +2230,7 @@
35D9DDE21DA25EEC00DAAD69 /* MGLCodingTests.m in Sources */,
DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */,
3598544D1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m in Sources */,
+ 071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */,
DA2DBBCE1D51E80400D38FF9 /* MGLStyleLayerTests.m in Sources */,
DA35A2C61CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */,
DAE7DEC21E245455007505A6 /* MGLNSStringAdditionsTests.m in Sources */,
@@ -2242,6 +2279,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 */,
@@ -2254,6 +2292,7 @@
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 */,
@@ -2272,6 +2311,7 @@
DD0902A91DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */,
35D13AB91D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */,
DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
+ 071BBB001EE7613F001FB02A /* MGLImageSource.mm in Sources */,
DA8848321CBAFA6200AB86E3 /* NSString+MGLAdditions.m in Sources */,
408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */,
DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */,
@@ -2283,7 +2323,6 @@
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 */,
355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
@@ -2339,6 +2378,7 @@
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 */,
@@ -2356,6 +2396,7 @@
DD0902AA1DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */,
DA35A2B41CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */,
35D13ABA1D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */,
+ 071BBAFF1EE7613E001FB02A /* MGLImageSource.mm in Sources */,
DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
408AA8591DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */,
DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */,
@@ -2685,6 +2726,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -2698,6 +2740,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
index 9dd128ff24..f5aff5b3b4 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
@@ -42,7 +42,7 @@
</AdditionalOptions>
</TestAction>
<LaunchAction
- buildConfiguration = "Debug"
+ buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml
index 31380faa2c..ba56c312eb 100644
--- a/platform/ios/jazzy.yml
+++ b/platform/ios/jazzy.yml
@@ -75,6 +75,7 @@ custom_categories:
children:
- MGLSource
- MGLTileSource
+ - MGLImageSource
- MGLShapeSource
- MGLRasterSource
- MGLVectorSource
@@ -108,6 +109,9 @@ custom_categories:
- MGLCoordinateBoundsMake
- MGLCoordinateBoundsOffset
- MGLCoordinateInCoordinateBounds
+ - MGLCoordinateQuad
+ - MGLCoordinateQuadMake
+ - MGLCoordinateQuadFromCoordinateBounds
- MGLCoordinateSpan
- MGLCoordinateSpanEqualToCoordinateSpan
- MGLCoordinateSpanMake
@@ -115,6 +119,7 @@ custom_categories:
- MGLDegreesFromRadians
- MGLRadiansFromDegrees
- MGLStringFromCoordinateBounds
+ - MGLStringFromCoordinateQuad
- name: Formatters
children:
- MGLClockDirectionFormatter
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 33d1146943..528606fd4e 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -271,13 +271,25 @@ MGL_EXPORT IB_DESIGNABLE
*/
@property (nonatomic, readonly) UIButton *attributionButton;
-@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("Use style.styleClasses.")));
+/**
+ 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.")));
-- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("Use style.hasStyleClass:.")));
+/**
+ Support for style classes has been removed. This property always returns NO.
+ */
+- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
-- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("Use style.addStyleClass:.")));
+/**
+ Support for style classes has been removed. This property is a no-op.
+ */
+- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
-- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("Use style.removeStyleClass:.")));
+/**
+ 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
@@ -602,7 +614,8 @@ MGL_EXPORT 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) IBInspectable double maximumZoomLevel;
@@ -1154,6 +1167,15 @@ MGL_EXPORT IB_DESIGNABLE
#pragma mark Overlaying the Map
/**
+ The complete list of overlays associated with the receiver. (read-only)
+
+ The objects in this array must adopt the `MGLOverlay` protocol. If no
+ overlays are associated with the map view, the value of this property is
+ empty array.
+ */
+@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id <MGLOverlay>) *overlays;
+
+/**
Adds a single overlay object to the map.
To remove an overlay from a map, use the `-removeOverlay:` method.
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 6acaa3c7f3..a03a5ad357 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>
@@ -15,11 +14,13 @@
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
+#include <mbgl/style/style.hpp>
#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/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>
@@ -39,6 +40,7 @@
#import "MGLMultiPoint_Private.h"
#import "MGLOfflineStorage_Private.h"
#import "MGLFoundation_Private.h"
+#import "MGLRendererFrontend.h"
#import "NSBundle+MGLAdditions.h"
#import "NSDate+MGLAdditions.h"
@@ -278,6 +280,8 @@ public:
{
mbgl::Map *_mbglMap;
MBGLView *_mbglView;
+ std::unique_ptr<MGLRenderFrontend> _rendererFrontend;
+
std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
BOOL _opaque;
@@ -378,7 +382,7 @@ public:
- (nonnull NSURL *)styleURL
{
- NSString *styleURLString = @(_mbglMap->getStyleURL().c_str()).mgl_stringOrNilIfEmpty;
+ NSString *styleURLString = @(_mbglMap->getStyle().getURL().c_str()).mgl_stringOrNilIfEmpty;
NSAssert(styleURLString || _isTargetingInterfaceBuilder, @"Invalid style URL string %@", styleURLString);
return styleURLString ? [NSURL URLWithString:styleURLString] : nil;
}
@@ -394,12 +398,12 @@ public:
styleURL = styleURL.mgl_URLByStandardizingScheme;
self.style = nil;
- _mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]);
+ _mbglMap->getStyle().loadURL([[styleURL absoluteString] UTF8String]);
}
- (IBAction)reloadStyle:(__unused id)sender {
NSURL *styleURL = self.styleURL;
- _mbglMap->setStyleURL("");
+ _mbglMap->getStyle().loadURL("");
self.styleURL = styleURL;
}
@@ -408,6 +412,11 @@ public:
return _mbglMap;
}
+- (mbgl::Renderer *)renderer
+{
+ return _rendererFrontend->getRenderer();
+}
+
- (void)commonInit
{
_isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
@@ -442,8 +451,10 @@ public:
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
_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, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique);
+ _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, *_mbglView);
+ _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
// start paused if in IB
if (_isTargetingInterfaceBuilder || background) {
@@ -748,49 +759,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;
@@ -1030,12 +1003,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];
}
@@ -2328,10 +2298,11 @@ public:
- (void)resetPosition
{
- CGFloat pitch = _mbglMap->getDefaultPitch();
- CLLocationDirection heading = mbgl::util::wrap(_mbglMap->getDefaultBearing(), 0., 360.);
- CLLocationDistance distance = MGLAltitudeForZoomLevel(_mbglMap->getDefaultZoom(), pitch, 0, self.frame.size);
- self.camera = [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(_mbglMap->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];
@@ -2339,7 +2310,7 @@ public:
- (void)emptyMemoryCache
{
- _mbglMap->onLowMemory();
+ _rendererFrontend->onLowMemory();
}
- (void)setZoomEnabled:(BOOL)zoomEnabled
@@ -2722,8 +2693,6 @@ public:
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
{
- _mbglMap->setMinZoom(minimumZoomLevel);
- [self validateTileCacheSize];
}
- (double)minimumZoomLevel
@@ -2734,7 +2703,6 @@ public:
- (void)setMaximumZoomLevel:(double)maximumZoomLevel
{
_mbglMap->setMaxZoom(maximumZoomLevel);
- [self validateTileCacheSize];
}
- (double)maximumZoomLevel
@@ -3612,7 +3580,7 @@ public:
annotationImage.delegate = self;
// add sprite
- _mbglMap->addAnnotationImage(iconIdentifier.UTF8String, annotationImage.image.mgl_styleImage);
+ _mbglMap->addAnnotationImage([annotationImage.image mgl_styleImageWithIdentifier:iconIdentifier]);
// Create a slop area with a “radius” equal in size to the annotation
// image’s alignment rect, allowing the eventual tap to be on any point
@@ -3695,6 +3663,22 @@ public:
}
}
+- (nonnull NS_ARRAY_OF(id <MGLOverlay>) *)overlays
+{
+ if (self.annotations == nil) { return @[]; }
+
+ NS_MUTABLE_ARRAY_OF(id <MGLOverlay>) *mutableOverlays = [NSMutableArray array];
+
+ [self.annotations enumerateObjectsUsingBlock:^(id<MGLAnnotation> _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) {
+ if ([annotation conformsToProtocol:@protocol(MGLOverlay)])
+ {
+ [mutableOverlays addObject:(id<MGLOverlay>)annotation];
+ }
+ }];
+
+ return [NSArray arrayWithArray:mutableOverlays];
+}
+
- (void)addOverlay:(id <MGLOverlay>)overlay
{
[self addOverlays:@[ overlay ]];
@@ -3898,7 +3882,7 @@ 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) },
});
@@ -4565,8 +4549,8 @@ public:
if (_showsUserHeadingIndicator)
{
self.showsUserLocation = YES;
- }
+ }
[self validateUserHeadingUpdating];
}
@@ -4941,7 +4925,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);
}
@@ -4974,7 +4958,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);
}
@@ -5200,7 +5184,7 @@ public:
return;
}
- self.style = [[MGLStyle alloc] initWithMapView:self];
+ self.style = [[MGLStyle alloc] initWithRawStyle:&_mbglMap->getStyle() mapView:self];
if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)])
{
[self.delegate mapView:self didFinishLoadingStyle:self.style];
@@ -5642,7 +5626,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_) {
@@ -5653,7 +5637,7 @@ public:
/// context state with the anticipated values.
void updateAssumedState() override {
assumeFramebufferBinding(ImplicitFramebufferBinding);
- assumeViewportSize(nativeView.framebufferSize);
+ assumeViewport(0, 0, nativeView.framebufferSize);
}
void bind() override {
@@ -5666,10 +5650,14 @@ public:
updateAssumedState();
} else {
// Our framebuffer is still bound, but the viewport might have changed.
- setViewportSize(nativeView.framebufferSize);
+ setViewport(0, 0, nativeView.framebufferSize);
}
}
+ 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];
@@ -5754,11 +5742,6 @@ public:
return reinterpret_cast<mbgl::gl::ProcAddress>(symbol);
}
- void invalidate() override
- {
- [nativeView setNeedsGLDisplay];
- }
-
void activate() override
{
if (activationCount++)
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/Mapbox.h b/platform/ios/src/Mapbox.h
index 67a26e8ed4..9b2c472cf6 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -52,6 +52,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLVectorSource.h"
#import "MGLShapeSource.h"
#import "MGLRasterSource.h"
+#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
#import "MGLTypes.h"
#import "MGLUserLocation.h"
@@ -59,3 +60,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 642355d412..3c179d6324 100644
--- a/platform/ios/src/UIImage+MGLAdditions.h
+++ b/platform/ios/src/UIImage+MGLAdditions.h
@@ -8,7 +8,11 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage;
-- (std::unique_ptr<mbgl::style::Image>)mgl_styleImage;
+- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage;
+
+- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier;
+
+- (mbgl::PremultipliedImage)mgl_premultipliedImage;
@end
diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm
index db64d78232..7cf1ed9bcc 100644
--- a/platform/ios/src/UIImage+MGLAdditions.mm
+++ b/platform/ios/src/UIImage+MGLAdditions.mm
@@ -6,14 +6,14 @@
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage
{
- CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->image.clone());
+ CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->getImage().clone());
if (!image) {
return nil;
}
- if (self = [self initWithCGImage:image scale:styleImage->pixelRatio orientation:UIImageOrientationUp])
+ if (self = [self initWithCGImage:image scale:styleImage->getPixelRatio() orientation:UIImageOrientationUp])
{
- if (styleImage->sdf)
+ if (styleImage->isSdf())
{
self = [self imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
@@ -22,10 +22,27 @@
return self;
}
-- (std::unique_ptr<mbgl::style::Image>)mgl_styleImage {
+- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage
+{
+ CGImageRef image = CGImageFromMGLPremultipliedImage(mbglImage.clone());
+ if (!image) {
+ return nil;
+ }
+
+ self = [self initWithCGImage:image scale:1.0 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>(MGLPremultipliedImageFromCGImage(self.CGImage),
+ return std::make_unique<mbgl::style::Image>([identifier UTF8String],
+ self.mgl_premultipliedImage,
float(self.scale), isTemplate);
}
+-(mbgl::PremultipliedImage)mgl_premultipliedImage {
+ return MGLPremultipliedImageFromCGImage(self.CGImage);
+}
@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..badbde408f 100644
--- a/platform/linux/config.cmake
+++ b/platform/linux/config.cmake
@@ -1,4 +1,4 @@
-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)
@@ -8,7 +8,7 @@ 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)
# Link with libuv. This is not part of loop-uv.cmake because loop-uv.cmake is also
@@ -69,6 +69,7 @@ macro(mbgl_platform_core)
PRIVATE platform/default/string_stdlib.cpp
PRIVATE platform/default/thread.cpp
PRIVATE platform/default/bidi.cpp
+ PRIVATE platform/default/thread_local.cpp
PRIVATE platform/default/utf.cpp
# Image handling
@@ -79,11 +80,11 @@ 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
diff --git a/platform/linux/scripts/coveralls.sh b/platform/linux/scripts/coveralls.sh
index b8ab73a24e..57affe1e28 100755
--- a/platform/linux/scripts/coveralls.sh
+++ b/platform/linux/scripts/coveralls.sh
@@ -3,9 +3,13 @@
set -e
set -o pipefail
+command -v lcov 2> /dev/null || {
+ echo "Aborting: lcov not found."
+ exit 1
+}
+
# Collect coverage data and save it into coverage.info
-mapbox_time "lcov_capture" \
-`scripts/mason.sh PREFIX lcov VERSION 1.12`/usr/bin/lcov \
+lcov \
--quiet \
--capture \
--no-external \
@@ -17,5 +21,8 @@ mapbox_time "lcov_capture" \
--base-directory "build/linux-x86_64/${BUILDTYPE}" \
--output-file "build/linux-x86_64/${BUILDTYPE}/coverage.info"
-mapbox_time "coveralls_upload" \
-coveralls-lcov "build/linux-x86_64/${BUILDTYPE}/coverage.info"
+coveralls-lcov \
+ --service-name="${COVERALLS_SERVICE_NAME}" \
+ --repo-token="${COVERALLS_REPO_TOKEN}" \
+ --service-job-id="${CIRCLE_BUILD_NUM}" \
+ "build/linux-x86_64/${BUILDTYPE}/coverage.info"
diff --git a/platform/linux/src/headless_backend_egl.cpp b/platform/linux/src/headless_backend_egl.cpp
index d98b2edc03..0784173af7 100644
--- a/platform/linux/src/headless_backend_egl.cpp
+++ b/platform/linux/src/headless_backend_egl.cpp
@@ -67,7 +67,7 @@ gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
bool HeadlessBackend::hasDisplay() {
if (!display) {
- display.reset(new HeadlessDisplay);
+ display = HeadlessDisplay::create();
}
return bool(display);
};
diff --git a/platform/linux/src/headless_backend_glx.cpp b/platform/linux/src/headless_backend_glx.cpp
index 36a60ec06b..0ba7f08630 100644
--- a/platform/linux/src/headless_backend_glx.cpp
+++ b/platform/linux/src/headless_backend_glx.cpp
@@ -17,7 +17,7 @@ struct GLXImpl : public HeadlessBackend::Impl {
fbConfigs(fbConfigs_) {
}
- ~GLXImpl() {
+ ~GLXImpl() override {
if (glxPbuffer) {
glXDestroyPbuffer(xDisplay, glxPbuffer);
}
@@ -50,7 +50,7 @@ gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
bool HeadlessBackend::hasDisplay() {
if (!display) {
- display.reset(new HeadlessDisplay);
+ display = HeadlessDisplay::create();
}
return bool(display);
};
@@ -58,8 +58,8 @@ bool HeadlessBackend::hasDisplay() {
void HeadlessBackend::createContext() {
assert(!hasContext());
- Display* xDisplay = display->attribute<Display*>();
- GLXFBConfig* fbConfigs = display->attribute<GLXFBConfig*>();
+ auto* xDisplay = display->attribute<Display*>();
+ auto* fbConfigs = display->attribute<GLXFBConfig*>();
// Try to create a legacy context.
GLXContext glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
@@ -81,7 +81,7 @@ void HeadlessBackend::createContext() {
};
GLXPbuffer glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes);
- impl.reset(new GLXImpl(glContext, glxPbuffer, xDisplay, fbConfigs));
+ impl = std::make_unique<mbgl::GLXImpl>(glContext, glxPbuffer, xDisplay, fbConfigs);
}
} // namespace mbgl
diff --git a/platform/linux/src/headless_display_egl.cpp b/platform/linux/src/headless_display_egl.cpp
index 03c8e16a59..b746211924 100644
--- a/platform/linux/src/headless_display_egl.cpp
+++ b/platform/linux/src/headless_display_egl.cpp
@@ -32,6 +32,9 @@ HeadlessDisplay::Impl::Impl() {
}
const EGLint attribs[] = {
+#if MBGL_USE_GLES2
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE
};
diff --git a/platform/linux/src/headless_display_glx.cpp b/platform/linux/src/headless_display_glx.cpp
index 4275ebb646..5dc342154d 100644
--- a/platform/linux/src/headless_display_glx.cpp
+++ b/platform/linux/src/headless_display_glx.cpp
@@ -27,7 +27,7 @@ HeadlessDisplay::Impl::Impl() {
throw std::runtime_error("Failed to open X display.");
}
- const char *extensions = reinterpret_cast<const char *>(glXQueryServerString(xDisplay, DefaultScreen(xDisplay), GLX_EXTENSIONS));
+ const auto *extensions = reinterpret_cast<const char *>(glXQueryServerString(xDisplay, DefaultScreen(xDisplay), GLX_EXTENSIONS));
if (!extensions) {
throw std::runtime_error("Cannot read GLX extensions.");
}
@@ -73,7 +73,6 @@ HeadlessDisplay::HeadlessDisplay()
: impl(std::make_unique<Impl>()) {
}
-HeadlessDisplay::~HeadlessDisplay() {
-}
+HeadlessDisplay::~HeadlessDisplay() = default;
} // namespace mbgl
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 5d8734356d..3ddffbb7fd 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -1,5 +1,37 @@
# Changelog for Mapbox macOS SDK
+## master
+
+### 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 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))
+
+### Other changes
+
+* 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.
diff --git a/platform/macos/app/Assets.xcassets/Radar/Contents.json b/platform/macos/app/Assets.xcassets/Radar/Contents.json
new file mode 100644
index 0000000000..da4a164c91
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/Contents.json
new file mode 100644
index 0000000000..ea096b04b8
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "southeast_radar_0.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/southeast_radar_0.png b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/southeast_radar_0.png
new file mode 100644
index 0000000000..c304b619c4
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_0.imageset/southeast_radar_0.png
Binary files differ
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/Contents.json
new file mode 100644
index 0000000000..a6a031ae2b
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "southeast_radar_1.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/southeast_radar_1.png b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/southeast_radar_1.png
new file mode 100644
index 0000000000..ed09fffbe1
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_1.imageset/southeast_radar_1.png
Binary files differ
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/Contents.json
new file mode 100644
index 0000000000..d607dda298
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "southeast_radar_2.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/southeast_radar_2.png b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/southeast_radar_2.png
new file mode 100644
index 0000000000..fee630f863
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_2.imageset/southeast_radar_2.png
Binary files differ
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/Contents.json b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/Contents.json
new file mode 100644
index 0000000000..9a110068a1
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "southeast_radar_3.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/southeast_radar_3.png b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/southeast_radar_3.png
new file mode 100644
index 0000000000..c4c7146afa
--- /dev/null
+++ b/platform/macos/app/Assets.xcassets/Radar/southeast_3.imageset/southeast_radar_3.png
Binary files differ
diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib
index 20a4f65b3f..9a53ba9d4b 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="Save snapshot" 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>
@@ -554,6 +560,12 @@
<action selector="insertCustomStyleLayer:" target="-1" id="LE5-lz-kx3"/>
</connections>
</menuItem>
+ <menuItem title="Add Animated Image Source" id="tjA-fT-GbA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="addAnimatedImageSource:" target="-1" id="TuN-Pa-hTG"/>
+ </connections>
+ </menuItem>
<menuItem title="Show All Annnotations" keyEquivalent="A" id="yMj-uM-8SN">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
@@ -648,7 +660,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"/>
@@ -724,7 +736,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"/>
@@ -733,7 +745,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/MapDocument.m b/platform/macos/app/MapDocument.m
index f494c91c18..36ca4ad228 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -3,6 +3,7 @@
#import "AppDelegate.h"
#import "LimeGreenStyleLayer.h"
#import "DroppedPinAnnotation.h"
+#import "MGLMapsnapshotter.h"
#import "MGLStyle+MBXAdditions.h"
#import "MGLVectorSource+MGLAdditions.h"
@@ -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,67 @@ 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.zoom = self.mapView.zoomLevel;
+
+ // Create and start the snapshotter
+ snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options];
+ [snapshotter startWithCompletionHandler: ^(NSImage *image, NSError *error) {
+ if (error) {
+ NSLog(@"Could not load snapshot: %@", [error localizedDescription]);
+ } else {
+ NSWindow* window = [[[self windowControllers] objectAtIndex:0] window];
+
+ NSString* newName = [[@"snapshot" stringByDeletingPathExtension] stringByAppendingPathExtension:@"png"];
+
+ // Set the default name for the file and show the panel.
+ NSSavePanel* panel = [NSSavePanel savePanel];
+ [panel setNameFieldStringValue:newName];
+ [panel beginSheetModalForWindow:window completionHandler:^(NSInteger result){
+ if (result == NSFileHandlingPanelOKButton) {
+ // Write the contents in the new format.
+ NSURL* fileURL = [panel URL];
+
+ NSBitmapImageRep *bitmapRep = nil;
+ for (NSImageRep *imageRep in [image representations]) {
+ if ([imageRep isKindOfClass:[NSBitmapImageRep class]]){
+ bitmapRep = (NSBitmapImageRep *)imageRep;
+ break; // stop on first bitmap rep we find
+ }
+ }
+
+ if (!bitmapRep) {
+ bitmapRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]];
+
+ }
+
+ NSString *extension = [[fileURL pathExtension] lowercaseString];
+ NSBitmapImageFileType fileType;
+ if ([extension isEqualToString:@"png"]) {
+ fileType = NSPNGFileType;
+ } else if ([extension isEqualToString:@"gif"]) {
+ fileType = NSGIFFileType;
+ } else if ([extension isEqualToString:@"jpg"] || [extension isEqualToString:@"jpeg"]) {
+ fileType = NSJPEGFileType;
+ } else {
+ fileType = NSTIFFFileType;
+ }
+
+ NSData *imageData = [bitmapRep representationUsingType:fileType properties:@{}];
+ [imageData writeToURL:fileURL atomically:NO];
+ }
+ }];
+
+ }
+ snapshotter = nil;
+ }];
+}
+
#pragma mark View methods
- (IBAction)showStyle:(id)sender {
@@ -572,6 +637,37 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
cos(angle) * 20);
}
+- (IBAction) addAnimatedImageSource:(id)sender {
+
+ MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:@"southeast_0"];
+
+ MGLCoordinateBounds bounds = { {22.551103322318994, -90.24006072802854}, {36.928147474567794, -75.1441643681673} };
+ MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"animated-radar-source" coordinateQuad:MGLCoordinateQuadFromCoordinateBounds(bounds) image:image];
+ [self.mapView.style addSource:imageSource];
+
+ MGLRasterStyleLayer * imageLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"animated-radar-layer" source:imageSource];
+ [self.mapView.style addLayer:imageLayer];
+
+ [NSTimer scheduledTimerWithTimeInterval:1.0
+ target:self
+ selector:@selector(updateAnimatedImageSource:)
+ userInfo:imageSource
+ repeats:YES];
+}
+
+
+- (void)updateAnimatedImageSource:(NSTimer *)timer {
+ static int radarSuffix = 0;
+ MGLImageSource *imageSource = (MGLImageSource *)timer.userInfo;
+
+ MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:[NSString stringWithFormat:@"southeast_%d", radarSuffix++]];
+ [imageSource setValue:image forKey:@"image"];
+
+ if(radarSuffix > 3) {
+ radarSuffix = 0 ;
+ }
+}
+
- (IBAction)insertCustomStyleLayer:(id)sender {
[self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
[self removeCustomStyleLayer:sender];
@@ -650,40 +746,58 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
MGLTransition transition = { .duration = 5, .delay = 1 };
self.mapView.style.transition = transition;
- MGLFillStyleLayer *fillStyleLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"water"];
-
+ MGLStyleLayer *waterLayer = [self.mapView.style layerWithIdentifier:@"water"];
MGLStyleValue *colorFunction = [MGLStyleValue<NSColor *> valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{
@0.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor redColor]],
@10.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor yellowColor]],
@20.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor blackColor]],
} options:nil];
- fillStyleLayer.fillColor = colorFunction;
+
+ if ([waterLayer respondsToSelector:@selector(fillColor)]) {
+ [waterLayer setValue:colorFunction forKey:@"fillColor"];
+ } else if ([waterLayer respondsToSelector:@selector(lineColor)]) {
+ [waterLayer setValue:colorFunction forKey:@"lineColor"];
+ }
NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"];
NSURL *geoJSONURL = [NSURL fileURLWithPath:filePath];
MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"ams" URL:geoJSONURL options:nil];
[self.mapView.style addSource:source];
- MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"test" source:source];
- fillLayer.fillColor = [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor greenColor]];
- fillLayer.predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"type", @"park"];
- [self.mapView.style addLayer:fillLayer];
+ MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"test" source:source];
+ circleLayer.circleColor = [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor greenColor]];
+ circleLayer.circleRadius = [MGLStyleValue<NSNumber *> valueWithRawValue:[NSNumber numberWithInteger:40]];
+// fillLayer.predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"type", @"park"];
+ [self.mapView.style addLayer:circleLayer];
+ MGLSource *streetsSource = [self.mapView.style sourceWithIdentifier:@"composite"];
+ if (streetsSource) {
NSImage *image = [NSImage imageNamed:NSImageNameIChatTheaterTemplate];
[self.mapView.style setImage:image forName:NSImageNameIChatTheaterTemplate];
- MGLSource *streetsSource = [self.mapView.style sourceWithIdentifier:@"composite"];
- MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource];
- theaterLayer.sourceLayerIdentifier = @"poi_label";
- theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"];
- theaterLayer.iconImageName = [MGLStyleValue valueWithRawValue:NSImageNameIChatTheaterTemplate];
- theaterLayer.iconScale = [MGLStyleValue valueWithRawValue:@2];
- theaterLayer.iconColor = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{
- @16.0: [MGLStyleValue valueWithRawValue:[NSColor redColor]],
- @18.0: [MGLStyleValue valueWithRawValue:[NSColor yellowColor]],
- @20.0: [MGLStyleValue valueWithRawValue:[NSColor blackColor]],
- } options:nil];
- [self.mapView.style addLayer:theaterLayer];
+ MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource];
+ theaterLayer.sourceLayerIdentifier = @"poi_label";
+ theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"];
+ theaterLayer.iconImageName = [MGLStyleValue valueWithRawValue:NSImageNameIChatTheaterTemplate];
+ theaterLayer.iconScale = [MGLStyleValue valueWithRawValue:@2];
+ theaterLayer.iconColor = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{
+ @16.0: [MGLStyleValue valueWithRawValue:[NSColor redColor]],
+ @18.0: [MGLStyleValue valueWithRawValue:[NSColor yellowColor]],
+ @20.0: [MGLStyleValue valueWithRawValue:[NSColor blackColor]],
+ } options:nil];
+ [self.mapView.style addLayer:theaterLayer];
+ }
+
+ NSURL *imageURL = [NSURL URLWithString:@"https://www.mapbox.com/mapbox-gl-js/assets/radar.gif"];
+ MGLCoordinateQuad quad = { {46.437, -80.425},
+ {37.936, -80.425},
+ {37.936, -71.516},
+ {46.437, -71.516} };
+ MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"radar-source" coordinateQuad:quad URL:imageURL];
+ [self.mapView.style addSource:imageSource];
+
+ MGLRasterStyleLayer * imageLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"radar-layer" source:imageSource];
+ [self.mapView.style addLayer:imageLayer];
}
- (IBAction)dropPin:(NSMenuItem *)sender {
@@ -892,6 +1006,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(drawAnimatedAnnotation:)) {
return !_isShowingAnimatedAnnotation;
}
+ if (menuItem.action == @selector(addAnimatedImageSource:)) {
+ return YES;
+ }
if (menuItem.action == @selector(insertCustomStyleLayer:)) {
return ![self.mapView.style layerWithIdentifier:@"mbx-custom"];
}
@@ -911,6 +1028,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;
}
diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake
index 1b8ea35814..bb2cc9ac1c 100644
--- a/platform/macos/config.cmake
+++ b/platform/macos/config.cmake
@@ -1,9 +1,9 @@
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10)
-mason_use(glfw VERSION 2017-02-09-77a8f10)
+mason_use(glfw VERSION 2017-07-13-67c9155)
mason_use(boost_libprogram_options VERSION 1.62.0)
mason_use(gtest VERSION 1.8.0)
-mason_use(benchmark VERSION 1.0.0-1)
+mason_use(benchmark VERSION 1.2.0)
mason_use(icu VERSION 58.1-min-size)
include(cmake/loop-darwin.cmake)
@@ -36,6 +36,7 @@ macro(mbgl_platform_core)
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
@@ -44,13 +45,17 @@ 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
@@ -90,6 +95,9 @@ macro(mbgl_platform_glfw)
target_link_libraries(mbgl-glfw
PRIVATE mbgl-loop-darwin
)
+ target_compile_options(mbgl-glfw
+ PRIVATE -fvisibility=hidden
+ )
endmacro()
@@ -97,6 +105,9 @@ macro(mbgl_platform_render)
target_link_libraries(mbgl-render
PRIVATE mbgl-loop-darwin
)
+ target_compile_options(mbgl-render
+ PRIVATE -fvisibility=hidden
+ )
endmacro()
@@ -104,6 +115,9 @@ macro(mbgl_platform_offline)
target_link_libraries(mbgl-offline
PRIVATE mbgl-loop-darwin
)
+ target_compile_options(mbgl-offline
+ PRIVATE -fvisibility=hidden
+ )
endmacro()
diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md
index b9163582b4..3cacc81376 100644
--- a/platform/macos/docs/guides/For Style Authors.md
+++ b/platform/macos/docs/guides/For Style Authors.md
@@ -96,7 +96,6 @@ the following terms for concepts defined in the style specification:
In the style specification | In the SDK
---------------------------|---------
-class | style class
filter | predicate
function type | interpolation mode
id | identifier
@@ -117,8 +116,9 @@ In style JSON | In the SDK
`geojson` | `MGLShapeSource`
`raster` | `MGLRasterSource`
`vector` | `MGLVectorSource`
+`image` | `MGLImageSource`
-`canvas`, `image`, and `video` sources are not supported.
+`canvas` and `video` sources are not supported.
### Tile sources
@@ -159,6 +159,12 @@ To create a shape source from local GeoJSON data, first
[convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects),
then use the `-[MGLShapeSource initWithIdentifier:shape:options:]` method.
+### Image sources
+
+Image sources accept a non-axis aligned quadrilateral as their geographic coordinates.
+These coordinates, in `MGLCoordinateQuad`, are described in counterclockwise order,
+in contrast to the clockwise order defined in the style specification.
+
## Configuring the map content’s appearance
Each layer defined by the style JSON file is represented at runtime by a style
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index fcb8155328..fcb0544479 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -7,6 +7,9 @@
objects = {
/* 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 */; };
+ 07BA4CAC1EE21887004528F5 /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */; };
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, ); }; };
@@ -78,7 +81,10 @@
558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */; };
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE7A51E56161C00C7916D /* MGLFoundation.mm */; };
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 */; };
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 */; };
@@ -272,6 +278,9 @@
/* End PBXCopyFilesBuildPhase section */
/* 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>"; };
+ 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLImageSourceTests.m; 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>"; };
@@ -347,7 +356,10 @@
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>"; };
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>"; };
@@ -671,6 +683,8 @@
DA8F25951D51CAC70010E6B5 /* MGLVectorSource.h */,
DA7DC9801DED5F5C0027472F /* MGLVectorSource_Private.h */,
DA8F25961D51CAC70010E6B5 /* MGLVectorSource.mm */,
+ 07A019EB1ED662D800ACD43E /* MGLImageSource.h */,
+ 07A019EC1ED662D800ACD43E /* MGLImageSource.mm */,
);
name = Sources;
sourceTree = "<group>";
@@ -779,6 +793,7 @@
DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */,
DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */,
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */,
+ 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */,
);
name = Sources;
sourceTree = "<group>";
@@ -1042,8 +1057,11 @@
558DE7A51E56161C00C7916D /* MGLFoundation.mm */,
DAE6C34D1CC31E0400DB3429 /* MGLMapCamera.h */,
DAE6C36E1CC31E2A00DB3429 /* MGLMapCamera.mm */,
+ 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */,
+ 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */,
DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */,
DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */,
+ 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */,
DAE6C3571CC31E0400DB3429 /* MGLStyle.h */,
3537CA731D3F93A600380318 /* MGLStyle_Private.h */,
DAE6C37A1CC31E2A00DB3429 /* MGLStyle.mm */,
@@ -1104,6 +1122,7 @@
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 */,
@@ -1149,6 +1168,7 @@
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 */,
@@ -1174,6 +1194,7 @@
DAE6C3B41CC31EF300DB3429 /* MGLCompassCell.h in Headers */,
DA87A99C1DC9D8DD00810D09 /* MGLShapeSource_Private.h in Headers */,
3537CA741D3F93A600380318 /* MGLStyle_Private.h in Headers */,
+ 0721493F1EE200E900085505 /* MGLImageSource.h in Headers */,
DA8F259A1D51CAD00010E6B5 /* MGLSource_Private.h in Headers */,
DA8F25931D51CA750010E6B5 /* MGLSymbolStyleLayer.h in Headers */,
DAE6C3B91CC31EF300DB3429 /* MGLOpenGLLayer.h in Headers */,
@@ -1394,6 +1415,8 @@
isa = PBXSourcesBuildPhase;
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 */,
@@ -1487,6 +1510,7 @@
DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */,
DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */,
DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */,
+ 07BA4CAC1EE21887004528F5 /* MGLImageSourceTests.m in Sources */,
DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */,
DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.mm in Sources */,
DA87A9A31DCACC5000810D09 /* MGLRasterStyleLayerTests.mm in Sources */,
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/src/MGLMapView.h b/platform/macos/src/MGLMapView.h
index 06fd0a67ea..04c2e77110 100644
--- a/platform/macos/src/MGLMapView.h
+++ b/platform/macos/src/MGLMapView.h
@@ -247,7 +247,8 @@ 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) IBInspectable double maximumZoomLevel;
@@ -730,6 +731,15 @@ MGL_EXPORT IB_DESIGNABLE
#pragma mark Overlaying the Map
/**
+ The complete list of overlays associated with the receiver. (read-only)
+
+ The objects in this array must adopt the `MGLOverlay` protocol. If no
+ overlays are associated with the map view, the value of this property is
+ empty array.
+ */
+@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id <MGLOverlay>) *overlays;
+
+/**
Adds a single overlay to the map.
To remove an overlay from a map, use the `-removeOverlay:` method.
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 1908a46cf9..eb4099f330 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -4,6 +4,7 @@
#import "MGLCompassCell.h"
#import "MGLOpenGLLayer.h"
#import "MGLStyle.h"
+#import "MGLRendererFrontend.h"
#import "MGLAnnotationImage_Private.h"
#import "MGLAttributionInfo_Private.h"
@@ -20,16 +21,18 @@
#import "MGLPolyline.h"
#import "MGLAnnotationImage.h"
#import "MGLMapViewDelegate.h"
+#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>
@@ -153,6 +156,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;
@@ -238,7 +242,7 @@ public:
// If the Style URL inspectable was not set, make sure to go through
// -setStyleURL: to load the default style.
- if (_mbglMap->getStyleURL().empty()) {
+ if (_mbglMap->getStyle().getURL().empty()) {
self.styleURL = nil;
}
}
@@ -268,8 +272,10 @@ public:
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];
+
+ auto renderer = std::make_unique<mbgl::Renderer>(*_mbglView, [NSScreen mainScreen].backingScaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::GLContextMode::Unique);
+ _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, *_mbglView, true);
+ _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, [NSScreen mainScreen].backingScaleFactor, *mbglFileSource, *_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.
@@ -606,7 +612,7 @@ public:
}
- (nonnull NSURL *)styleURL {
- NSString *styleURLString = @(_mbglMap->getStyleURL().c_str()).mgl_stringOrNilIfEmpty;
+ NSString *styleURLString = @(_mbglMap->getStyle().getURL().c_str()).mgl_stringOrNilIfEmpty;
return styleURLString ? [NSURL URLWithString:styleURLString] : [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion];
}
@@ -628,12 +634,12 @@ public:
styleURL = styleURL.mgl_URLByStandardizingScheme;
self.style = nil;
- _mbglMap->setStyleURL(styleURL.absoluteString.UTF8String);
+ _mbglMap->getStyle().loadURL(styleURL.absoluteString.UTF8String);
}
- (IBAction)reloadStyle:(__unused id)sender {
NSURL *styleURL = self.styleURL;
- _mbglMap->setStyleURL("");
+ _mbglMap->getStyle().loadURL("");
self.styleURL = styleURL;
}
@@ -641,6 +647,10 @@ public:
return _mbglMap;
}
+- (mbgl::Renderer *)renderer {
+ return _rendererFrontend->getRenderer();
+}
+
#pragma mark View hierarchy and drawing
- (void)viewWillMoveToWindow:(NSWindow *)newWindow {
@@ -685,9 +695,6 @@ public:
- (void)setFrame:(NSRect)frame {
super.frame = frame;
- if (!NSEqualRects(frame, self.frame)) {
- [self validateTileCacheSize];
- }
if (!_isTargetingInterfaceBuilder) {
_mbglMap->setSize(self.size);
}
@@ -771,11 +778,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;
@@ -787,21 +791,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();
@@ -936,20 +925,23 @@ public:
return;
}
- self.style = [[MGLStyle alloc] initWithMapView:self];
+ self.style = [[MGLStyle alloc] initWithRawStyle:&_mbglMap->getStyle() mapView:self];
if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)])
{
[self.delegate mapView:self didFinishLoadingStyle:self.style];
}
}
-- (void)sourceDidChange {
+- (void)sourceDidChange:(MGLSource *)source {
if (!_mbglMap) {
return;
}
-
- [self installAttributionView];
+ // Attribution only applies to tiled sources
+ if ([source isKindOfClass:[MGLTileSource class]]) {
+ [self installAttributionView];
+ }
self.needsUpdateConstraints = YES;
+ self.needsDisplay = YES;
}
#pragma mark Printing
@@ -1056,13 +1048,11 @@ public:
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
{
_mbglMap->setMinZoom(minimumZoomLevel);
- [self validateTileCacheSize];
}
- (void)setMaximumZoomLevel:(double)maximumZoomLevel
{
_mbglMap->setMaxZoom(maximumZoomLevel);
- [self validateTileCacheSize];
}
- (double)maximumZoomLevel {
@@ -1954,7 +1944,7 @@ public:
return;
}
- _mbglMap->addAnnotationImage(iconIdentifier.UTF8String, annotationImage.image.mgl_styleImage);
+ _mbglMap->addAnnotationImage([annotationImage.image mgl_styleImageWithIdentifier:iconIdentifier]);
// Create a slop area with a “radius” equal to the annotation image’s entire
// size, allowing the eventual click to be on any point within this image.
@@ -2139,7 +2129,7 @@ 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) },
});
@@ -2418,6 +2408,22 @@ public:
#pragma mark Overlays
+- (nonnull NS_ARRAY_OF(id <MGLOverlay>) *)overlays
+{
+ if (self.annotations == nil) { return @[]; }
+
+ NS_MUTABLE_ARRAY_OF(id <MGLOverlay>) *mutableOverlays = [NSMutableArray array];
+
+ [self.annotations enumerateObjectsUsingBlock:^(id<MGLAnnotation> _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) {
+ if ([annotation conformsToProtocol:@protocol(MGLOverlay)])
+ {
+ [mutableOverlays addObject:(id<MGLOverlay>)annotation];
+ }
+ }];
+
+ return [NSArray arrayWithArray:mutableOverlays];
+}
+
- (void)addOverlay:(id <MGLOverlay>)overlay {
[self addOverlays:@[overlay]];
}
@@ -2544,7 +2550,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);
}
@@ -2578,7 +2584,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);
}
@@ -2756,10 +2762,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;
@@ -2831,8 +2836,10 @@ public:
[nativeView mapViewDidFinishLoadingStyle];
}
- void onSourceChanged(mbgl::style::Source&) override {
- [nativeView sourceDidChange];
+ void onSourceChanged(mbgl::style::Source& source) override {
+ NSString *identifier = @(source.getID().c_str());
+ MGLSource * nativeSource = [nativeView.style sourceWithIdentifier:identifier];
+ [nativeView sourceDidChange:nativeSource];
}
mbgl::gl::ProcAddress initializeExtension(const char* name) override {
@@ -2848,10 +2855,6 @@ public:
return reinterpret_cast<mbgl::gl::ProcAddress>(symbol);
}
- void invalidate() override {
- [nativeView setNeedsGLDisplay];
- }
-
void activate() override {
if (activationCount++) {
return;
@@ -2872,12 +2875,16 @@ public:
void updateAssumedState() override {
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
assumeFramebufferBinding(fbo);
- assumeViewportSize(nativeView.framebufferSize);
+ assumeViewport(0, 0, nativeView.framebufferSize);
}
void bind() override {
setFramebufferBinding(fbo);
- setViewportSize(nativeView.framebufferSize);
+ setViewport(0, 0, nativeView.framebufferSize);
+ }
+
+ mbgl::Size getFramebufferSize() const override {
+ return nativeView.framebufferSize;
}
mbgl::PremultipliedImage readStillImage() {
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 0f47dace70..a082a4771e 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -50,8 +50,10 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLVectorSource.h"
#import "MGLShapeSource.h"
#import "MGLRasterSource.h"
+#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
#import "MGLTypes.h"
#import "NSValue+MGLAdditions.h"
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
+#import "MGLMapSnapshotter.h"
diff --git a/platform/macos/src/NSImage+MGLAdditions.h b/platform/macos/src/NSImage+MGLAdditions.h
index d3cc80615b..c08fc57bea 100644
--- a/platform/macos/src/NSImage+MGLAdditions.h
+++ b/platform/macos/src/NSImage+MGLAdditions.h
@@ -10,7 +10,9 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)image;
-- (std::unique_ptr<mbgl::style::Image>)mgl_styleImage;
+- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier;
+
+- (mbgl::PremultipliedImage) mgl_premultipliedImage;
@end
diff --git a/platform/macos/src/NSImage+MGLAdditions.mm b/platform/macos/src/NSImage+MGLAdditions.mm
index 91c4f7bf66..7500a8a207 100644
--- a/platform/macos/src/NSImage+MGLAdditions.mm
+++ b/platform/macos/src/NSImage+MGLAdditions.mm
@@ -16,33 +16,34 @@
}
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage {
- CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->image.clone());
+ CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->getImage().clone());
if (!image) {
return nil;
}
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:image];
CGImageRelease(image);
- if (self = [self initWithSize:NSMakeSize(styleImage->getWidth(), styleImage->getHeight())]) {
+ CGFloat w = styleImage->getImage().size.width / styleImage->getPixelRatio();
+ CGFloat h = styleImage->getImage().size.height / styleImage->getPixelRatio();
+ if (self = [self initWithSize:NSMakeSize(w, h)]) {
[self addRepresentation:rep];
- [self setTemplate:styleImage->sdf];
+ [self setTemplate:styleImage->isSdf()];
}
return self;
}
-- (std::unique_ptr<mbgl::style::Image>)mgl_styleImage {
- // 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 std::make_unique<mbgl::style::Image>(std::move(cPremultipliedImage),
- (float)(rep.pixelsWide / self.size.width),
- [self isTemplate]);
+- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier {
+ mbgl::PremultipliedImage cPremultipliedImage = self.mgl_premultipliedImage;
+ auto imageWidth = cPremultipliedImage.size.width;
+ return std::make_unique<mbgl::style::Image>([identifier UTF8String],
+ std::move(cPremultipliedImage),
+ (float)(imageWidth / self.size.width),
+ [self isTemplate]);
+}
+
+- (mbgl::PremultipliedImage)mgl_premultipliedImage {
+ 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 e77ceff31f..e75b1687f1 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,3 +1,22 @@
+# master
+* 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))
+
+# 3.5.3 - May 30, 2017
+
+- Fixed a regression around `line-dasharrary` and `fill-pattern` that caused these properties to sometimes not render correctly ([#9130](https://github.com/mapbox/mapbox-gl-native/pull/9130))
+
+# 3.5.2 - May 18, 2017
+
+- Fixed a memory leak ([#8884](https://github.com/mapbox/mapbox-gl-native/pull/9035))
+
+
# 3.5.1 - May 8, 2017
- Adds Node v6 binaries. **Note, Node v4 binaries will be removed on August 1st.** ([#8884](https://github.com/mapbox/mapbox-gl-native/pull/8884))
diff --git a/platform/node/README.md b/platform/node/README.md
index 545d87861f..d19b2a9343 100644
--- a/platform/node/README.md
+++ b/platform/node/README.md
@@ -1,6 +1,6 @@
# node-mapbox-gl-native
-[![NPM](https://nodei.co/npm/mapbox-gl-native.png)](https://npmjs.org/package/mapbox-gl-native)
+[![NPM](https://nodei.co/npm/@mapbox/mapbox-gl-native.png)](https://npmjs.org/package/@mapbox/mapbox-gl-native)
## Installing
@@ -14,7 +14,7 @@ By default, installs binaries. On these platforms no additional dependencies are
Run:
```
-npm install mapbox-gl-native
+npm install @mapbox/mapbox-gl-native
```
Other platforms will fall back to a source compile with `make node`; see INSTALL.md in the repository root directory for prequisites.
@@ -31,7 +31,7 @@ npm run test-suite
```js
var fs = require('fs');
var path = require('path');
-var mbgl = require('mapbox-gl-native');
+var mbgl = require('@mapbox/mapbox-gl-native');
var sharp = require('sharp');
var options = {
diff --git a/platform/node/bitrise.yml b/platform/node/bitrise.yml
index 00005f0f36..fab3093d6e 100644
--- a/platform/node/bitrise.yml
+++ b/platform/node/bitrise.yml
@@ -64,7 +64,7 @@ workflows:
brew install cmake awscli node@4 node@6
brew link node@4 --force
gem install xcpretty --no-rdoc --no-ri
- export BUILDTYPE=Release
+ export BUILDTYPE=RelWithDebInfo
export PUBLISH=true
make test-node && ./platform/node/scripts/after_success.sh
brew unlink node@4
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_success.sh b/platform/node/scripts/after_success.sh
index ae34446927..18e00a4f0d 100755
--- a/platform/node/scripts/after_success.sh
+++ b/platform/node/scripts/after_success.sh
@@ -3,10 +3,13 @@
set -e
set -o pipefail
-if [[ -n ${PUBLISH:-} ]]; then
- if [[ "${BUILDTYPE}" == "Release" ]]; then
+if [[ "${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 22daedef6a..d266745548 100644
--- a/platform/node/src/node_conversion.hpp
+++ b/platform/node/src/node_conversion.hpp
@@ -82,6 +82,14 @@ inline optional<float> toNumber(v8::Local<v8::Value> value) {
return value->NumberValue();
}
+inline optional<double> toDouble(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->NumberValue();
+}
+
inline optional<std::string> toString(v8::Local<v8::Value> value) {
Nan::HandleScope scope;
if (!value->IsString()) {
diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp
index 9bae86b76a..8a3927e2cf 100644
--- a/platform/node/src/node_geojson.hpp
+++ b/platform/node/src/node_geojson.hpp
@@ -1,13 +1,15 @@
#include <mbgl/style/conversion/geojson.hpp>
+#include <string>
namespace mbgl {
namespace style {
namespace conversion {
template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>&, Error& error) const {
- error = { "not implemented" };
- return {};
+optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>& value, Error& error) const {
+ Nan::JSON JSON;
+ std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked());
+ return convert<GeoJSON>(string, error);
}
} // namespace conversion
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 1c8bf6d1d2..9a7ebce445 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -4,14 +4,15 @@
#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/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>
@@ -24,27 +25,18 @@ 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 };
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);
}
@@ -60,8 +52,8 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
Nan::SetPrototypeMethod(tpl, "release", Release);
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
- Nan::SetPrototypeMethod(tpl, "addClass", AddClass);
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);
@@ -79,9 +71,6 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
constructor.Reset(tpl->GetFunction());
Nan::Set(target, Nan::New("Map").ToLocalChecked(), tpl->GetFunction());
-
- // Initialize display connection on module load.
- sharedDisplay();
}
/**
@@ -163,25 +152,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 +185,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 {
@@ -214,11 +194,26 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
try {
- nodeMap->map->setStyleJSON(style);
+ nodeMap->map->getStyle().loadJSON(style);
} catch (const std::exception &ex) {
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();
@@ -266,11 +261,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()) {
@@ -278,7 +273,7 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
const int length = classes->Length();
options.classes.reserve(length);
for (int i = 0; i < length; i++) {
- options.classes.push_back(std::string { *Nan::Utf8String(Nan::To<v8::String>(Nan::Get(classes, i).ToLocalChecked()).ToLocalChecked()) });
+ options.classes.emplace_back(std::string { *Nan::Utf8String(Nan::To<v8::String>(Nan::Get(classes, i).ToLocalChecked()).ToLocalChecked()) });
}
}
@@ -366,48 +361,22 @@ 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->getClasses() != options.classes) {
- map->setClasses(options.classes);
- }
-
- if (map->getZoom() != options.zoom) {
- map->setZoom(options.zoom);
- }
-
- mbgl::LatLng latLng(options.latitude, options.longitude);
- if (map->getLatLng() != latLng) {
- map->setLatLng(latLng);
- }
-
- if (map->getBearing() != options.bearing) {
- map->setBearing(options.bearing);
- }
+ 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;
- if (map->getPitch() != options.pitch) {
- map->setPitch(options.pitch);
- }
-
- 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);
}
});
@@ -506,7 +475,7 @@ void NodeMap::release() {
uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) {
delete reinterpret_cast<uv_async_t *>(h);
});
-
+
map.reset();
}
@@ -532,37 +501,24 @@ void NodeMap::Cancel(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
void NodeMap::cancel() {
- auto style = map->getStyleJSON();
+ 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, mbgl::MapMode::Still);
// FIXME: Reload the style after recreating the map. We need to find
// a better way of canceling an ongoing rendering on the core level
// without resetting the map, which is way too expensive.
- map->setStyleJSON(style);
+ map->getStyle().loadJSON(style);
error = std::make_exception_ptr(std::runtime_error("Canceled"));
renderFinished();
}
-void NodeMap::AddClass(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]->IsString()) {
- return Nan::ThrowTypeError("First argument must be a string");
- }
-
- try {
- nodeMap->map->addClass(*Nan::Utf8String(info[0]));
- } catch (const std::exception &ex) {
- return Nan::ThrowError(ex.what());
- }
-
- info.GetReturnValue().SetUndefined();
-}
-
void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
using namespace mbgl::style;
using namespace mbgl::style::conversion;
@@ -578,6 +534,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) {
@@ -585,7 +545,25 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return;
}
- nodeMap->map->addSource(std::move(*source));
+ 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) {
@@ -606,7 +584,7 @@ void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return;
}
- nodeMap->map->addLayer(std::move(*layer));
+ nodeMap->map->getStyle().addLayer(std::move(*layer));
}
void NodeMap::RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -624,7 +602,7 @@ void NodeMap::RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
- nodeMap->map->removeLayer(*Nan::Utf8String(info[0]));
+ nodeMap->map->getStyle().removeLayer(*Nan::Utf8String(info[0]));
}
void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -660,7 +638,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("width parameter required");
}
- if (!Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->IsUint32()) {
+ if (!Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->IsNumber()) {
return Nan::ThrowTypeError("pixelRatio parameter required");
}
@@ -671,7 +649,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("Max height and width is 1024");
}
- uint32_t pixelRatio = Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->Uint32Value();
+ float pixelRatio = Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->NumberValue();
auto imageBuffer = Nan::To<v8::Object>(info[1]).ToLocalChecked()->ToObject();
char * imageDataBuffer = node::Buffer::Data(imageBuffer);
@@ -686,7 +664,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
mbgl::UnassociatedImage cImage({ imageWidth, imageHeight}, std::move(data));
mbgl::PremultipliedImage cPremultipliedImage = mbgl::util::premultiply(std::move(cImage));
- nodeMap->map->addImage(*Nan::Utf8String(info[0]), std::make_unique<mbgl::style::Image>(std::move(cPremultipliedImage), pixelRatio));
+ nodeMap->map->getStyle().addImage(std::make_unique<mbgl::style::Image>(*Nan::Utf8String(info[0]), std::move(cPremultipliedImage), pixelRatio));
}
void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -704,7 +682,7 @@ void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
- nodeMap->map->removeImage(*Nan::Utf8String(info[0]));
+ nodeMap->map->getStyle().removeImage(*Nan::Utf8String(info[0]));
}
void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -722,7 +700,7 @@ void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info
return Nan::ThrowTypeError("First argument must be a string");
}
- mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
if (!layer) {
return Nan::ThrowTypeError("layer not found");
}
@@ -754,7 +732,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info)
return Nan::ThrowTypeError("First argument must be a string");
}
- mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
if (!layer) {
return Nan::ThrowTypeError("layer not found");
}
@@ -763,12 +741,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info)
return Nan::ThrowTypeError("Second argument must be a string");
}
- mbgl::optional<std::string> klass;
- if (info.Length() == 4 && info[3]->IsString()) {
- klass = std::string(*Nan::Utf8String(info[3]));
- }
-
- mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2], klass);
+ mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2]);
if (error) {
return Nan::ThrowTypeError(error->message.c_str());
}
@@ -812,7 +785,7 @@ void NodeMap::SetFilter(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
- mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
if (!layer) {
return Nan::ThrowTypeError("layer not found");
}
@@ -911,6 +884,8 @@ void NodeMap::DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
nodeMap->map->dumpDebugLogs();
+ nodeMap->frontend->getRenderer()->dumpDebugLogs();
+
info.GetReturnValue().SetUndefined();
}
@@ -947,7 +922,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
auto layers = layersOption.As<v8::Array>();
std::vector<std::string> layersVec;
for (uint32_t i=0; i < layers->Length(); i++) {
- layersVec.push_back(*Nan::Utf8String(Nan::Get(layers,i).ToLocalChecked()));
+ layersVec.emplace_back(*Nan::Utf8String(Nan::Get(layers,i).ToLocalChecked()));
}
queryOptions.layerIDs = layersVec;
}
@@ -972,7 +947,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()
@@ -983,7 +958,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);
@@ -1007,9 +982,12 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
.ToLocalChecked()
->NumberValue()
: 1.0;
- }()),
- map(std::make_unique<mbgl::Map>(backend,
- mbgl::Size{ 256, 256 },
+ }())
+ , 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,
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index cdc8f1e51f..c8e33bb347 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,
@@ -42,8 +44,8 @@ public:
static void Render(const Nan::FunctionCallbackInfo<v8::Value>&);
static void Release(const Nan::FunctionCallbackInfo<v8::Value>&);
static void Cancel(const Nan::FunctionCallbackInfo<v8::Value>&);
- static void AddClass(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>&);
@@ -69,9 +71,9 @@ public:
std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback);
const float pixelRatio;
- NodeBackend backend;
- std::unique_ptr<mbgl::OffscreenView> view;
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_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/ignores.json b/platform/node/test/ignores.json
new file mode 100644
index 0000000000..1084a5c923
--- /dev/null
+++ b/platform/node/test/ignores.json
@@ -0,0 +1,59 @@
+{
+ "query-tests/geometry/multilinestring": "needs investigation",
+ "query-tests/geometry/multipolygon": "needs investigation",
+ "query-tests/geometry/polygon": "needs investigation",
+ "query-tests/regressions/mapbox-gl-js#3534": "https://github.com/mapbox/mapbox-gl-native/issues/8193",
+ "query-tests/regressions/mapbox-gl-js#4417": "https://github.com/mapbox/mapbox-gl-native/issues/8007",
+ "query-tests/symbol-features-in/pitched-screen": "https://github.com/mapbox/mapbox-gl-native/issues/6817",
+ "query-tests/symbol-features-in/tilted-inside": "https://github.com/mapbox/mapbox-gl-native/issues/5056",
+ "query-tests/symbol-features-in/tilted-outside": "https://github.com/mapbox/mapbox-gl-native/issues/9435",
+ "query-tests/world-wrapping/box": "skip - needs issue",
+ "query-tests/world-wrapping/point": "skip - needs issue",
+ "render-tests/debug/collision-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/tile-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/tile": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/extent/1024-circle": "needs investigation",
+ "render-tests/extent/1024-symbol": "needs investigation",
+ "render-tests/fill-extrusion-pattern/@2x": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/function-2": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/function": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/literal": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/missing": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary",
+ "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js",
+ "render-tests/icon-size/composite-function-high-base-plain": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
+ "render-tests/icon-size/composite-function-high-base-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
+ "render-tests/icon-text-fit/both-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/both": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/height": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/width-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/width": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/line-width/property-function": "https://github.com/mapbox/mapbox-gl-js/issues/3682#issuecomment-264348200",
+ "render-tests/line-join/property-function": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
+ "render-tests/line-join/property-function-dasharray": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
+ "render-tests/line-opacity/step-curve": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
+ "render-tests/regressions/mapbox-gl-js#3682": "https://github.com/mapbox/mapbox-gl-js/issues/3682",
+ "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357",
+ "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847",
+ "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
+ "render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
+ "render-tests/symbol-placement/line": "needs issue",
+ "render-tests/text-font/camera-function": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/text-keep-upright/line-placement-true-offset": "https://github.com/mapbox/mapbox-gl-native/issues/9271",
+ "render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/auto-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/map-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/viewport-overzoomed": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
+ "render-tests/text-pitch-alignment/viewport-overzoomed-single-glyph": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
+ "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-size/composite-expression": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601"
+}
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index 4ab76b937a..39665e41ef 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -108,8 +108,8 @@ test('Map', function(t) {
'render',
'release',
'cancel',
- 'addClass',
'addSource',
+ 'removeSource',
'addLayer',
'removeLayer',
'addImage',
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 8ac372b7c3..323f429bed 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -10,23 +10,27 @@ 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});
- }
- });
+ if (options.recycleMap) {
+ if (maps.has(options.pixelRatio)) {
+ var map = maps.get(options.pixelRatio);
+ map.request = mapRequest;
+ } else {
+ maps.set(options.pixelRatio, new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest
+ }));
+ var map = maps.get(options.pixelRatio);
}
- });
+ } else {
+ var map = new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest
+ });
+ }
var timedOut = false;
var watchdog = setTimeout(function () {
@@ -46,14 +50,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,16 +90,25 @@ module.exports = function (style, options, callback) {
applyOperations(operations.slice(1), callback);
});
- } else if (operation[0] === 'addImage') {
+ } 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);
+
+ } else if (operation[0] === 'setStyle') {
+ map.load(operation[1]);
applyOperations(operations.slice(1), callback);
+
} else {
// Ensure that the next `map.render(options)` does not overwrite this change.
if (operation[0] === 'setCenter') {
diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp
index a72faf005e..c4efbfa318 100644
--- a/platform/qt/app/mapwindow.cpp
+++ b/platform/qt/app/mapwindow.cpp
@@ -79,8 +79,6 @@ void MapWindow::changeStyle()
void MapWindow::keyPressEvent(QKeyEvent *ev)
{
- static const qint64 transitionDuration = 300;
-
switch (ev->key()) {
case Qt::Key_S:
changeStyle();
@@ -92,6 +90,9 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
m_sourceAdded = true;
+ // Not in all styles, but will work on streets
+ QString before = "waterway-label";
+
QFile geojson(":source1.geojson");
geojson.open(QIODevice::ReadOnly);
@@ -106,7 +107,7 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
routeCase["id"] = "routeCase";
routeCase["type"] = "line";
routeCase["source"] = "routeSource";
- m_map->addLayer(routeCase);
+ m_map->addLayer(routeCase, before);
m_map->setPaintProperty("routeCase", "line-color", QColor("white"));
m_map->setPaintProperty("routeCase", "line-width", 20.0);
@@ -118,7 +119,7 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
route["id"] = "route";
route["type"] = "line";
route["source"] = "routeSource";
- m_map->addLayer(route);
+ m_map->addLayer(route, before);
m_map->setPaintProperty("route", "line-color", QColor("blue"));
m_map->setPaintProperty("route", "line-width", 8.0);
@@ -253,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());
@@ -268,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());
@@ -277,28 +288,13 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
}
}
break;
- case Qt::Key_4: {
- if (m_styleSourcedAnnotationId.isNull()) {
- QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 });
- QMapbox::Coordinate topRight = m_map->coordinateForPixel({ 0, qreal(size().height()) });
- QMapbox::Coordinate bottomLeft = m_map->coordinateForPixel({ qreal(size().width()), 0 });
- QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) });
- QMapbox::CoordinatesCollections geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } };
- QMapbox::StyleSourcedAnnotation styleSourced { { QMapbox::ShapeAnnotationGeometry::PolygonType, geometry }, "water" };
- m_styleSourcedAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::StyleSourcedAnnotation>(styleSourced));
- } else {
- m_map->removeAnnotation(m_styleSourcedAnnotationId.toUInt());
- m_styleSourcedAnnotationId.clear();
- }
- }
- break;
case Qt::Key_5: {
if (m_map->layerExists("circleLayer")) {
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";
@@ -319,14 +315,6 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
case Qt::Key_Tab:
m_map->cycleDebugOptions();
break;
- case Qt::Key_R: {
- m_map->setTransitionOptions(transitionDuration);
- if (m_map->hasClass("night")) {
- m_map->removeClass("night");
- } else {
- m_map->addClass("night");
- }
- } break;
default:
break;
}
diff --git a/platform/qt/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp
index 6b4b7fd1cc..c484114ec0 100644
--- a/platform/qt/app/mapwindow.hpp
+++ b/platform/qt/app/mapwindow.hpp
@@ -64,7 +64,6 @@ private:
QVariant m_symbolAnnotationId;
QVariant m_lineAnnotationId;
QVariant m_fillAnnotationId;
- QVariant m_styleSourcedAnnotationId;
bool m_sourceAdded = false;
};
diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake
index 0f2bab0516..732fb1de28 100644
--- a/platform/qt/config.cmake
+++ b/platform/qt/config.cmake
@@ -52,12 +52,12 @@ endmacro()
macro(mbgl_platform_test)
target_sources(mbgl-test
+ 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.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
diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp
index e69b37108d..d138f4057b 100644
--- a/platform/qt/include/qmapbox.hpp
+++ b/platform/qt/include/qmapbox.hpp
@@ -26,6 +26,11 @@ struct Q_DECL_EXPORT Feature {
LineStringType,
PolygonType
};
+
+ 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 +39,15 @@ struct Q_DECL_EXPORT Feature {
struct Q_DECL_EXPORT ShapeAnnotationGeometry {
enum Type {
- LineStringType,
+ LineStringType = 1,
PolygonType,
MultiLineStringType,
MultiPolygonType
};
+
+ ShapeAnnotationGeometry(Type type_ = LineStringType, const CoordinatesCollections& geometry_ = CoordinatesCollections())
+ : type(type_), geometry(geometry_) {}
+
Type type;
CoordinatesCollections geometry;
};
@@ -49,22 +58,25 @@ struct Q_DECL_EXPORT SymbolAnnotation {
};
struct Q_DECL_EXPORT LineAnnotation {
+ 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 {
- ShapeAnnotationGeometry geometry;
- float opacity = 1.0f;
- QColor color = Qt::black;
- QVariant outlineColor;
-};
+ 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_) {}
-struct Q_DECL_EXPORT StyleSourcedAnnotation {
ShapeAnnotationGeometry geometry;
- QString layerID;
+ float opacity;
+ QColor color;
+ QVariant outlineColor;
};
typedef QVariant Annotation;
@@ -109,6 +121,5 @@ Q_DECLARE_METATYPE(QMapbox::SymbolAnnotation);
Q_DECLARE_METATYPE(QMapbox::ShapeAnnotationGeometry);
Q_DECLARE_METATYPE(QMapbox::LineAnnotation);
Q_DECLARE_METATYPE(QMapbox::FillAnnotation);
-Q_DECLARE_METATYPE(QMapbox::StyleSourcedAnnotation);
#endif // QMAPBOX_H
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
index 00c5735a93..e2fb283989 100644
--- a/platform/qt/include/qmapboxgl.hpp
+++ b/platform/qt/include/qmapboxgl.hpp
@@ -168,12 +168,6 @@ public:
void setGestureInProgress(bool inProgress);
- void addClass(const QString &);
- void removeClass(const QString &);
- bool hasClass(const QString &) const;
- void setClasses(const QStringList &);
- QStringList getClasses() const;
-
void setTransitionOptions(qint64 duration, qint64 delay = 0);
void addAnnotationIcon(const QString &name, const QImage &sprite);
@@ -183,7 +177,7 @@ public:
void removeAnnotation(QMapbox::AnnotationID);
void setLayoutProperty(const QString &layer, const QString &property, const QVariant &value);
- void setPaintProperty(const QString &layer, const QString &property, const QVariant &value, const QString &klass = QString());
+ void setPaintProperty(const QString &layer, const QString &property, const QVariant &value);
bool isFullyLoaded() const;
@@ -219,8 +213,8 @@ public:
QMapbox::CustomLayerRenderFunction,
QMapbox::CustomLayerDeinitializeFunction,
void* context,
- char* before = NULL);
- void addLayer(const QVariantMap &params);
+ const QString& before = QString());
+ void addLayer(const QVariantMap &params, const QString& before = QString());
bool layerExists(const QString &id);
void removeLayer(const QString &id);
diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake
index 67622efca6..489ae5ed08 100644
--- a/platform/qt/qt.cmake
+++ b/platform/qt/qt.cmake
@@ -36,6 +36,9 @@ 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
@@ -43,7 +46,7 @@ set(MBGL_QT_FILES
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_image.cpp
PRIVATE platform/qt/src/run_loop.cpp
PRIVATE platform/qt/src/run_loop_impl.hpp
PRIVATE platform/qt/src/sqlite3.cpp
@@ -64,6 +67,8 @@ add_library(qmapboxgl SHARED
platform/qt/src/qmapbox.cpp
platform/qt/src/qmapboxgl.cpp
platform/qt/src/qmapboxgl_p.hpp
+ platform/qt/src/qmapboxgl_renderer_frontend_p.hpp
+ platform/qt/src/qmapboxgl_renderer_frontend_p.cpp
platform/default/mbgl/util/default_styles.hpp
)
@@ -75,6 +80,8 @@ add_executable(mbgl-qt
platform/qt/resources/common.qrc
)
+xcode_create_scheme(TARGET mbgl-qt)
+
if(WITH_QT_4)
include(platform/qt/qt4.cmake)
else()
@@ -90,13 +97,17 @@ if (MASON_PLATFORM STREQUAL "osx" OR MASON_PLATFORM STREQUAL "ios")
PRIVATE "-framework Foundation"
PRIVATE "-framework OpenGL"
)
-else()
+elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
list(APPEND MBGL_QT_FILES
PRIVATE platform/default/thread.cpp
)
list(APPEND MBGL_QT_LIBRARIES
PRIVATE -lGL
)
+elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+ list(APPEND MBGL_QT_FILES
+ PRIVATE platform/qt/src/thread.cpp
+ )
endif()
add_custom_command(
@@ -106,3 +117,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/src/http_request.cpp b/platform/qt/src/http_request.cpp
index 6141216c65..386a2d9ef4 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;
@@ -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") {
diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp
index 410e114690..aad32a35dc 100644
--- a/platform/qt/src/qmapbox.cpp
+++ b/platform/qt/src/qmapbox.cpp
@@ -139,16 +139,6 @@ namespace QMapbox {
*/
/*!
- \class QMapbox::StyleSourcedAnnotation
-
- \inmodule Mapbox Qt SDK
-
- Represents a style sourced annotation object, along with its properties.
-
- A style sourced annotation comprises of its geometry and a layer identifier.
-*/
-
-/*!
\typedef QMapbox::Annotation
Alias for QVariant.
@@ -226,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 ce7e237afb..e79c6af56a 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,8 +8,9 @@
#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>
@@ -16,6 +18,8 @@
#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>
@@ -69,18 +73,21 @@ namespace {
QThreadStorage<std::shared_ptr<mbgl::util::RunLoop>> loop;
-// Conversion helper functions.
+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();
-auto fromQStringList(const QStringList &list)
-{
- std::vector<std::string> strings;
- strings.reserve(list.size());
- for (const QString &string : list) {
- strings.push_back(string.toStdString());
+ if (!fs) {
+ weak = fs = std::make_shared<mbgl::DefaultFileSource>(
+ cachePath, assetRoot, maximumCacheSize);
}
- return strings;
+
+ return fs;
}
+// Conversion helper functions.
+
mbgl::Size sanitizedSize(const QSize& size) {
return mbgl::Size {
mbgl::util::max(0u, static_cast<uint32_t>(size.width())),
@@ -88,7 +95,7 @@ mbgl::Size sanitizedSize(const QSize& size) {
};
};
-std::unique_ptr<mbgl::style::Image> toStyleImage(const QImage &sprite) {
+std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage &sprite) {
const QImage swapped = sprite
.rgbSwapped()
.convertToFormat(QImage::Format_ARGB32_Premultiplied);
@@ -97,6 +104,7 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QImage &sprite) {
memcpy(img.get(), swapped.constBits(), swapped.byteCount());
return std::make_unique<mbgl::style::Image>(
+ id.toStdString(),
mbgl::PremultipliedImage(
{ static_cast<uint32_t>(swapped.width()), static_cast<uint32_t>(swapped.height()) },
std::move(img)),
@@ -114,6 +122,11 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QImage &sprite) {
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
*/
@@ -434,13 +447,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());
@@ -483,12 +496,12 @@ void QMapboxGL::cycleDebugOptions()
*/
QString QMapboxGL::styleJson() const
{
- return QString::fromStdString(d_ptr->mapObj->getStyleJSON());
+ return QString::fromStdString(d_ptr->mapObj->getStyle().getJSON());
}
void QMapboxGL::setStyleJson(const QString &style)
{
- d_ptr->mapObj->setStyleJSON(style.toStdString());
+ d_ptr->mapObj->getStyle().loadJSON(style.toStdString());
}
/*!
@@ -508,12 +521,12 @@ void QMapboxGL::setStyleJson(const QString &style)
*/
QString QMapboxGL::styleUrl() const
{
- return QString::fromStdString(d_ptr->mapObj->getStyleURL());
+ return QString::fromStdString(d_ptr->mapObj->getStyle().getURL());
}
void QMapboxGL::setStyleUrl(const QString &url)
{
- d_ptr->mapObj->setStyleURL(url.toStdString());
+ d_ptr->mapObj->getStyle().loadURL(url.toStdString());
}
/*!
@@ -571,7 +584,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() });
}
/*!
@@ -752,98 +765,32 @@ void QMapboxGL::setGestureInProgress(bool progress)
}
/*!
- Adds an \a className to the list of active classes. Layers tagged with a certain class
- will only be active when the class is added.
-
- This was removed from the \l {https://www.mapbox.com/mapbox-gl-style-spec/#layer-paint.*}
- {Mapbox style specification} and should no longer be used.
-
- \deprecated
- \sa removeClass()
-*/
-void QMapboxGL::addClass(const QString &className)
-{
- d_ptr->mapObj->addClass(className.toStdString());
-}
-
-/*!
- Removes a \a className.
-
- \deprecated
- \sa addClass()
-*/
-void QMapboxGL::removeClass(const QString &className)
-{
- d_ptr->mapObj->removeClass(className.toStdString());
-}
-
-/*!
- Returns true when \a className is active, false otherwise.
-
- \deprecated
- \sa addClass()
-*/
-bool QMapboxGL::hasClass(const QString &className) const
-{
- return d_ptr->mapObj->hasClass(className.toStdString());
-}
-
-/*!
- Bulk adds a list of \a classNames.
-
- \deprecated
- \sa addClass()
-*/
-void QMapboxGL::setClasses(const QStringList &classNames)
-{
- d_ptr->mapObj->setClasses(fromQStringList(classNames));
-}
-
-/*!
- Returns a list of active classes.
-
- \deprecated
- \sa setClasses()
-*/
-QStringList QMapboxGL::getClasses() const
-{
- QStringList classNames;
- for (const std::string &mbglClass : d_ptr->mapObj->getClasses()) {
- classNames << QString::fromStdString(mbglClass);
- }
- return classNames;
-}
-
-/*!
- Sets the \a duration and \a delay of style class transitions. Style property
- values transition to new values with animation when a new class is set.
-
- \deprecated
- \sa addClass()
+ Sets the \a duration and \a delay of style transitions. Style paint property
+ values transition to new values with animation when they are updated.
*/
void QMapboxGL::setTransitionOptions(qint64 duration, qint64 delay) {
static auto convert = [](qint64 value) -> mbgl::optional<mbgl::Duration> {
return std::chrono::duration_cast<mbgl::Duration>(mbgl::Milliseconds(value));
};
- d_ptr->mapObj->setTransitionOptions(mbgl::style::TransitionOptions{ convert(duration), convert(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;
@@ -852,23 +799,20 @@ 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 }, {}) };
}
- } else if (annotation.canConvert<QMapbox::StyleSourcedAnnotation>()) {
- QMapbox::StyleSourcedAnnotation styleSourcedAnnotation = annotation.value<QMapbox::StyleSourcedAnnotation>();
- return mbgl::StyleSourcedAnnotation { asMapboxGLGeometry(styleSourcedAnnotation.geometry), styleSourcedAnnotation.layerID.toStdString() };
}
qWarning() << "Unable to convert annotation:" << annotation;
@@ -884,7 +828,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));
}
/*!
@@ -894,7 +838,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));
}
/*!
@@ -906,7 +850,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.
@@ -940,30 +884,27 @@ 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;
- Layer* layer_ = d_ptr->mapObj->getLayer(layer.toStdString());
+ Layer* layer_ = d_ptr->mapObj->getStyle().getLayer(layer.toStdString());
if (!layer_) {
qWarning() << "Layer not found:" << layer;
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.
- The argument \a styleClass is deprecated and is used for defining the style class for the paint
- property.
-
For paint properties that take a color as \a value, such as \c fill-color, a string such as
\c blue can be passed or a QColor.
@@ -1009,23 +950,18 @@ 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, const QString& styleClass)
+void QMapboxGL::setPaintProperty(const QString& layer, const QString& property_, const QVariant& value)
{
using namespace mbgl::style;
- Layer* layer_ = d_ptr->mapObj->getLayer(layer.toStdString());
+ Layer* layer_ = d_ptr->mapObj->getStyle().getLayer(layer.toStdString());
if (!layer_) {
qWarning() << "Layer not found:" << layer;
return;
}
- mbgl::optional<std::string> klass;
- if (!styleClass.isEmpty()) {
- klass = styleClass.toStdString();
- }
-
- if (conversion::setPaintProperty(*layer_, property.toStdString(), value, klass)) {
- qWarning() << "Error setting paint property:" << layer << "-" << property;
+ if (conversion::setPaintProperty(*layer_, property_.toStdString(), value)) {
+ qWarning() << "Error setting paint property:" << layer << "-" << property_;
return;
}
}
@@ -1056,7 +992,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() });
}
/*!
@@ -1094,7 +1030,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) {
@@ -1114,15 +1050,15 @@ void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &icon)
{
if (icon.isNull()) return;
- d_ptr->mapObj->addAnnotationImage(name.toStdString(), toStyleImage(icon));
+ d_ptr->mapObj->addAnnotationImage(toStyleImage(name, 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_);
}
/*!
@@ -1267,7 +1203,7 @@ void QMapboxGL::addSource(const QString &id, const QVariantMap &params)
return;
}
- d_ptr->mapObj->addSource(std::move(*source));
+ d_ptr->mapObj->getStyle().addSource(std::move(*source));
}
/*!
@@ -1275,7 +1211,7 @@ void QMapboxGL::addSource(const QString &id, const QVariantMap &params)
*/
bool QMapboxGL::sourceExists(const QString& sourceID)
{
- return !!d_ptr->mapObj->getSource(sourceID.toStdString());
+ return !!d_ptr->mapObj->getStyle().getSource(sourceID.toStdString());
}
/*!
@@ -1289,7 +1225,7 @@ void QMapboxGL::updateSource(const QString &id, const QVariantMap &params)
using namespace mbgl::style;
using namespace mbgl::style::conversion;
- auto source = d_ptr->mapObj->getSource(id.toStdString());
+ auto source = d_ptr->mapObj->getStyle().getSource(id.toStdString());
if (!source) {
addSource(id, params);
return;
@@ -1319,8 +1255,8 @@ void QMapboxGL::removeSource(const QString& id)
{
auto sourceIDStdString = id.toStdString();
- if (d_ptr->mapObj->getSource(sourceIDStdString)) {
- d_ptr->mapObj->removeSource(sourceIDStdString);
+ if (d_ptr->mapObj->getStyle().getSource(sourceIDStdString)) {
+ d_ptr->mapObj->getStyle().removeSource(sourceIDStdString);
}
}
@@ -1337,9 +1273,9 @@ void QMapboxGL::addCustomLayer(const QString &id,
QMapbox::CustomLayerRenderFunction renderFn,
QMapbox::CustomLayerDeinitializeFunction deinitFn,
void *context,
- char *before)
+ const QString& before)
{
- d_ptr->mapObj->addLayer(std::make_unique<mbgl::style::CustomLayer>(
+ d_ptr->mapObj->getStyle().addLayer(std::make_unique<mbgl::style::CustomLayer>(
id.toStdString(),
reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initFn),
// This cast is safe as long as both mbgl:: and QMapbox::
@@ -1347,13 +1283,14 @@ void QMapboxGL::addCustomLayer(const QString &id,
(mbgl::style::CustomLayerRenderFunction)renderFn,
reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitFn),
context),
- before ? mbgl::optional<std::string>(before) : mbgl::optional<std::string>());
+ before.isEmpty() ? mbgl::optional<std::string>() : mbgl::optional<std::string>(before.toStdString()));
}
/*!
Adds a style layer to the map as specified by the \l
{https://www.mapbox.com/mapbox-gl-style-spec/#root-layers}{Mapbox style specification} with
- \a params.
+ \a params. The layer will be added under the layer specified by \a before, if specified.
+ Otherwise it will be added as the topmost layer.
This example shows how to add a layer that will be used to show a route line on the map. Note
that nothing will be drawn until we set paint properties using setPaintProperty().
@@ -1369,7 +1306,7 @@ void QMapboxGL::addCustomLayer(const QString &id,
/note The source must exist prior to adding a layer.
*/
-void QMapboxGL::addLayer(const QVariantMap &params)
+void QMapboxGL::addLayer(const QVariantMap &params, const QString& before)
{
using namespace mbgl::style;
using namespace mbgl::style::conversion;
@@ -1381,7 +1318,8 @@ void QMapboxGL::addLayer(const QVariantMap &params)
return;
}
- d_ptr->mapObj->addLayer(std::move(*layer));
+ d_ptr->mapObj->getStyle().addLayer(std::move(*layer),
+ before.isEmpty() ? mbgl::optional<std::string>() : mbgl::optional<std::string>(before.toStdString()));
}
/*!
@@ -1389,7 +1327,7 @@ void QMapboxGL::addLayer(const QVariantMap &params)
*/
bool QMapboxGL::layerExists(const QString& id)
{
- return !!d_ptr->mapObj->getLayer(id.toStdString());
+ return !!d_ptr->mapObj->getStyle().getLayer(id.toStdString());
}
/*!
@@ -1397,7 +1335,7 @@ bool QMapboxGL::layerExists(const QString& id)
*/
void QMapboxGL::removeLayer(const QString& id)
{
- d_ptr->mapObj->removeLayer(id.toStdString());
+ d_ptr->mapObj->getStyle().removeLayer(id.toStdString());
}
/*!
@@ -1414,7 +1352,7 @@ void QMapboxGL::addImage(const QString &id, const QImage &image)
{
if (image.isNull()) return;
- d_ptr->mapObj->addImage(id.toStdString(), toStyleImage(image));
+ d_ptr->mapObj->getStyle().addImage(toStyleImage(id, image));
}
/*!
@@ -1422,7 +1360,7 @@ void QMapboxGL::addImage(const QString &id, const QImage &image)
*/
void QMapboxGL::removeImage(const QString &id)
{
- d_ptr->mapObj->removeImage(id.toStdString());
+ d_ptr->mapObj->getStyle().removeImage(id.toStdString());
}
/*!
@@ -1450,7 +1388,7 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter)
using namespace mbgl::style;
using namespace mbgl::style::conversion;
- Layer* layer_ = d_ptr->mapObj->getLayer(layer.toStdString());
+ Layer* layer_ = d_ptr->mapObj->getStyle().getLayer(layer.toStdString());
if (!layer_) {
qWarning() << "Layer not found:" << layer;
return;
@@ -1491,10 +1429,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.
@@ -1509,11 +1447,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();
}
/*!
@@ -1552,21 +1487,30 @@ 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 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()));
@@ -1584,18 +1528,18 @@ QMapboxGLPrivate::~QMapboxGLPrivate()
{
}
-mbgl::Size QMapboxGLPrivate::framebufferSize() const {
+mbgl::Size QMapboxGLPrivate::getFramebufferSize() const {
return sanitizedSize(fbSize);
}
void QMapboxGLPrivate::updateAssumedState() {
assumeFramebufferBinding(fbObject);
- assumeViewportSize(framebufferSize());
+ assumeViewport(0, 0, getFramebufferSize());
}
void QMapboxGLPrivate::bind() {
setFramebufferBinding(fbObject);
- setViewportSize(framebufferSize());
+ setViewport(0, 0, getFramebufferSize());
}
void QMapboxGLPrivate::invalidate()
@@ -1606,6 +1550,11 @@ void QMapboxGLPrivate::invalidate()
}
}
+void QMapboxGLPrivate::render()
+{
+ frontend->render();
+}
+
void QMapboxGLPrivate::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode)
{
if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) {
@@ -1680,7 +1629,7 @@ void QMapboxGLPrivate::onDidFinishLoadingStyle()
void QMapboxGLPrivate::onSourceChanged(mbgl::style::Source&)
{
std::string attribution;
- for (const auto& source : mapObj->getSources()) {
+ for (const auto& source : mapObj->getStyle().getSources()) {
// Avoid duplicates by using the most complete attribution HTML snippet.
if (source->getAttribution() && (attribution.size() < source->getAttribution()->size()))
attribution = *source->getAttribution();
diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp
index 49a7942cce..eb86870c10 100644
--- a/platform/qt/src/qmapboxgl_p.hpp
+++ b/platform/qt/src/qmapboxgl_p.hpp
@@ -1,10 +1,10 @@
#pragma once
#include "qmapboxgl.hpp"
+#include "qmapboxgl_renderer_frontend_p.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>
@@ -12,7 +12,7 @@
#include <QObject>
#include <QSize>
-class QMapboxGLPrivate : public QObject, public mbgl::View, public mbgl::Backend
+class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver
{
Q_OBJECT
@@ -20,14 +20,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,8 +49,9 @@ 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 };
@@ -63,6 +61,8 @@ private:
public slots:
void connectionEstablished();
+ void invalidate();
+ void render();
signals:
void needsRendering();
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 4b93ca7423..40d7e5b928 100644
--- a/platform/qt/src/qt_conversion.hpp
+++ b/platform/qt/src/qt_conversion.hpp
@@ -83,6 +83,13 @@ inline optional<float> toNumber(const QVariant& value) {
return {};
}
}
+inline optional<double> toDouble(const QVariant& value) {
+ if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
+ return value.toDouble();
+ } else {
+ return {};
+ }
+}
inline optional<std::string> toString(const QVariant& value) {
if (value.type() == QVariant::String) {
diff --git a/platform/qt/src/image.cpp b/platform/qt/src/qt_image.cpp
index 403ca9cbd3..403ca9cbd3 100644
--- a/platform/qt/src/image.cpp
+++ b/platform/qt/src/qt_image.cpp
diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp
index c44f284852..71ea19032a 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() {
diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp
index 8df279c25d..7d47ae552b 100644
--- a/platform/qt/src/sqlite3.cpp
+++ b/platform/qt/src/sqlite3.cpp
@@ -11,6 +11,7 @@
#include <cstring>
#include <cstdio>
#include <chrono>
+#include <limits>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/logging.hpp>
@@ -50,6 +51,15 @@ 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." };
+ }
+}
+
class DatabaseImpl {
public:
DatabaseImpl(const char* filename, int flags) {
@@ -77,7 +87,7 @@ public:
db->setDatabaseName(QString(filename));
if (!db->open()) {
- checkDatabaseError(*db);
+ checkDatabaseOpenError(*db);
}
}
@@ -132,7 +142,11 @@ Database::~Database() {
void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
assert(impl);
- std::string timeoutStr = mbgl::util::toString(timeout.count());
+
+ // 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);
+
QString connectOptions = impl->db->connectOptions();
if (connectOptions.isEmpty()) {
if (!connectOptions.isEmpty()) connectOptions.append(';');
@@ -143,7 +157,7 @@ void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
}
impl->db->setConnectOptions(connectOptions);
if (!impl->db->open()) {
- checkDatabaseError(*impl->db);
+ checkDatabaseOpenError(*impl->db);
}
}
@@ -298,6 +312,7 @@ bool Statement::run() {
return impl->query.next();
}
+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
index 453604076e..747f8796fa 100644
--- a/platform/qt/test/qmapboxgl.cpp
+++ b/platform/qt/test/qmapboxgl.cpp
@@ -15,7 +15,7 @@ class QMapboxGLTest : public QObject, public ::testing::Test {
Q_OBJECT
public:
- QMapboxGLTest() : fbo((assert(widget.context()->isValid()), widget.makeCurrent(), QSize(512, 512))), map(nullptr, settings) {
+ 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()),
@@ -38,6 +38,7 @@ public:
private:
QGLWidget widget;
+ const QSize size;
QGLFramebufferObject fbo;
protected:
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/clang-tools.sh b/scripts/clang-tools.sh
index 2a330a5ee4..bdda4544b9 100755
--- a/scripts/clang-tools.sh
+++ b/scripts/clang-tools.sh
@@ -3,58 +3,62 @@
set -e
set -o pipefail
-CLANG_TIDY=${CLANG_TIDY:-$(scripts/mason.sh PREFIX clang-tidy VERSION 3.9.1)/bin/clang-tidy}
-CLANG_FORMAT=${CLANG_FORMAT:-$(scripts/mason.sh PREFIX clang-format VERSION 3.9.1)/bin/clang-format}
+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}
-command -v ${CLANG_TIDY} >/dev/null 2>&1 || {
- echo "Can't find ${CLANG_TIDY} in PATH."
- if [ -z ${CLANG_TIDY} ]; then
- echo "Alternatively, you can set CLANG_TIDY to point to clang-tidy."
- fi
- exit 1
-}
+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 || {
+ echo "Can't find ${CLANG_FILE} in PATH."
+ if [ -z ${CLANG_FILE} ]; then
+ echo "Alternatively, you can manually set ${!CLANG_FILE@}."
+ fi
+ exit 1
+ }
+done
cd $1
-CDUP=$(git rev-parse --show-cdup)
+export CDUP=$(git rev-parse --show-cdup)
+export CLANG_TIDY CLANG_APPLY CLANG_FORMAT
-function check_tidy() {
- echo "Running clang-tidy on $0..."
- if [[ -n $1 ]] && [[ $1 == "--fix" ]]; then
- OUTPUT=$(${CLANG_TIDY} -p=$PWD -fix -fix-errors ${0} 2>/dev/null)
- else
- OUTPUT=$(${CLANG_TIDY} -p=$PWD ${0} 2>/dev/null)
- fi
- if [[ -n $OUTPUT ]]; then
- echo "Caught clang-tidy warning/error:"
- echo -e "$OUTPUT"
+function run_clang_tidy() {
+ FILES=$(git ls-files "src/mbgl/*.cpp" "platform/*.cpp" "test/*.cpp")
+ ${CLANG_TIDY_PREFIX}/share/run-clang-tidy.py -j ${JOBS} \
+ -clang-tidy-binary ${CLANG_TIDY} \
+ -clang-apply-replacements-binary ${CLANG_APPLY} \
+ -fix ${FILES} 2>/dev/null || exit 1
+}
+
+function run_clang_tidy_diff() {
+ OUTPUT=$(git diff origin/master --src-prefix=${CDUP} --dst-prefix=${CDUP} | \
+ ${CLANG_TIDY_PREFIX}/share/clang-tidy-diff.py \
+ -clang-tidy-binary ${CLANG_TIDY} \
+ 2>/dev/null)
+ if [[ -n $OUTPUT ]] && [[ $OUTPUT != "No relevant changes found." ]]; then
+ echo -e "${OUTPUT}"
exit 1
fi
}
-function check_format() {
+function run_clang_format() {
echo "Running clang-format on $0..."
- ${CLANG_FORMAT} -i ${CDUP}/$0
+ DIFF_FILES=$(git diff origin/master --name-only *cpp)
+ echo "${DIFF_FILES}" | xargs -I{} -P ${JOBS} bash -c 'run_clang_format' {}
+ ${CLANG_FORMAT} -i ${CDUP}/$0 || exit 1
}
-export CLANG_TIDY CLANG_FORMAT
-export -f check_tidy check_format
+export -f run_clang_tidy run_clang_tidy_diff run_clang_format
-echo "Running clang checks... (this might take a while)"
+echo "Running Clang checks... (this might take a while)"
if [[ -n $2 ]] && [[ $2 == "--diff" ]]; then
- DIFF_FILES=$(for file in `git diff origin/master --name-only | grep "pp$"`; do echo $file; done)
- if [[ -n $DIFF_FILES ]]; then
- echo "${DIFF_FILES}" | xargs -I{} -P ${JOBS} bash -c 'check_tidy --fix' {}
- # XXX disabled until we run clang-format over the entire codebase.
- #echo "${DIFF_FILES}" | xargs -I{} -P ${JOBS} bash -c 'check_format' {}
- git diff --quiet || {
- echo "Changes were made to source files - please review them before committing."
- exit 1
- }
- fi
- echo "All looks good!"
+ run_clang_tidy_diff $@
+ # XXX disabled until we run clang-format over the entire codebase.
+ #run_clang_format $@
+ echo "All checks pass!"
else
- git ls-files "${CDUP}/src/mbgl/*.cpp" "${CDUP}/platform/*.cpp" "${CDUP}/test/*.cpp" | \
- xargs -I{} -P ${JOBS} bash -c 'check_tidy' {}
+ run_clang_tidy $@
fi
diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js
index 159817f62f..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,90 +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" ? `
-#ifdef 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" ? `
-#ifdef 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.
@@ -145,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 c169c4ecd5..fe9a1a906b 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';
}
@@ -53,7 +56,7 @@ global.evaluatedType = function (property) {
}
};
-function attributeType(property, type) {
+function attributeUniformType(property, type) {
const attributeNameExceptions = {
'text-opacity': 'opacity',
'icon-opacity': 'opacity',
@@ -64,11 +67,12 @@ function attributeType(property, type) {
'text-halo-blur': 'halo_blur',
'icon-halo-blur': 'halo_blur',
'text-halo-width': 'halo_width',
- 'icon-halo-width': 'halo_width'
+ 'icon-halo-width': 'halo_width',
+ 'line-gap-width': 'gapwidth'
}
const name = attributeNameExceptions[property.name] ||
property.name.replace(type + '-', '').replace(/-/g, '_');
- return `attributes::a_${name}${name === 'offset' ? '<1>' : ''}`;
+ return `attributes::a_${name}${name === 'offset' ? '<1>' : ''}, uniforms::u_${name}`;
}
global.layoutPropertyType = function (property) {
@@ -81,7 +85,7 @@ global.layoutPropertyType = function (property) {
global.paintPropertyType = function (property, type) {
if (isDataDriven(property)) {
- return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeType(property, type)}>`;
+ return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`;
} else if (/-pattern$/.test(property.name) || property.name === 'line-dasharray') {
return `CrossFadedPaintProperty<${evaluatedType(property)}>`;
} else {
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/travis_helper.sh b/scripts/travis_helper.sh
deleted file mode 100755
index 5a765b131d..0000000000
--- a/scripts/travis_helper.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env bash
-
-# This script is sourced, so do not set -e or -o pipefail here. Doing so would
-# bleed into Travis' wrapper script, which messes with their workflow, e.g.
-# preventing after_failure scripts to be triggered.
-
-case `uname -s` in
- 'Darwin') JOBS=$((`sysctl -n hw.ncpu` + 2)) ;;
- 'Linux') JOBS=$((`nproc` + 2)) ;;
- *) JOBS=2 ;;
-esac
-
-ANSI_CLEAR="\e[0m"
-
-function mapbox_time_start {
- local name=$1
- mapbox_timer_name=$name
-
- mapbox_fold start $name
-
- mapbox_timer_id=$(printf %08x $(( RANDOM * RANDOM )))
- eval "mapbox_start_time_$mapbox_timer_id=$(mapbox_nanoseconds)"
- echo -en "travis_time:start:$mapbox_timer_id\n"
-}
-
-function mapbox_time_finish {
- local name=${1:-$mapbox_timer_name}
- local timer_id=${2:-$mapbox_timer_id}
- local timer_start="mapbox_start_time_$timer_id"
- eval local start_time=\${$timer_start}
- local end_time=$(mapbox_nanoseconds)
- local duration=$(($end_time-$start_time))
- echo -en "travis_time:end:$timer_id:start=$start_time,finish=$end_time,duration=$duration\n"
-
- mapbox_fold end $name
-}
-
-function mapbox_time {
- local name=$1 ; shift
- mapbox_time_start $name
- local timer_id=$mapbox_timer_id
- echo "\$ $@"
- $@
- mapbox_time_finish $name $timer_id
-}
-
-function mapbox_fold {
- local action=$1
- local name=$2
- echo -en "travis_fold:${action}:${name}\r${ANSI_CLEAR}"
-}
-
-function mapbox_nanoseconds {
- local cmd="date"
- local format="+%s%N"
- local os=$(uname)
-
- if hash gdate > /dev/null 2>&1; then
- cmd="gdate" # use gdate if available
- elif [[ "$os" = Darwin ]]; then
- format="+%s000000000" # fallback to second precision on darwin (does not support %N)
- fi
-
- $cmd -u $format
-}
-
-export JOBS
-export ANSI_CLEAR
-export -f mapbox_fold
-export -f mapbox_nanoseconds
-export -f mapbox_time
-export -f mapbox_time_start
-export -f mapbox_time_finish
diff --git a/scripts/travis_setup.sh b/scripts/travis_setup.sh
deleted file mode 100755
index 20a708d20a..0000000000
--- a/scripts/travis_setup.sh
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env bash
-# This script is sourced; do not set -e or -o pipefail here.
-
-if [ ! -z "${_CXX}" ]; then export CXX="${_CXX}" ; fi
-if [ ! -z "${_CC}" ]; then export CC="${_CC}" ; fi
-
-if [ "${CCACHE:-0}" -ge 1 ]; then
- export CXX="ccache ${CXX}"
- export CC="ccache ${CC}"
-
- # 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.
- if [ $(echo | ${CXX} -dM -E - | grep -c "#define __clang__ 1") -ge 1 ]; then
- export CXX="${CXX} -Qunused-arguments -fcolor-diagnostics"
- export CC="${CC} -Qunused-arguments -fcolor-diagnostics"
- else
- # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60304
- # GCC normally throws this error which is in GTest, but *only* when compilation and
- # preprocessing aren't combined in one step. However, when using ccache with GCC, we are
- # running them in separate steps, so this warning/error is shown.
- export CXX="${CXX} -Wno-conversion-null"
- export CC="${CC} -Wno-conversion-null"
- fi
-fi
-
-echo "export CXX=\"${CXX}\""
-echo "export CC=\"${CC}\""
-if [ -x $(which ${CXX}) ]; then
- ${CXX} --version
-fi
-
-# Touch package.json so that we are definitely going to run an npm update action
-mapbox_time "touch_package_json" \
-touch package.json
-
-function mapbox_install_logbt {
- scripts/mason.sh INSTALL gdb VERSION 7.12
- 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=.
- sudo ./logbt --setup
- ./logbt --test
-}
-
-function mapbox_start_xvfb {
- if [ ! -f /etc/init.d/xvfb ]; then
- echo "Error: Could not start Xvfb mock server."
- exit 1
- fi
-
- mapbox_time "start_xvfb" \
- sh -e /etc/init.d/xvfb start
- sleep 2 # sometimes, xvfb takes some time to start up
-
- # Make sure we're connecting to xvfb
- export DISPLAY=:99.0
-}
-
-export -f mapbox_start_xvfb
-
-function mapbox_export_mesa_library_path {
- # Install and set up to load a more recent version of mesa
- mapbox_time "install_mesa" \
- scripts/mason.sh install mesa VERSION 13.0.4
-
- 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/valgrind.sup b/scripts/valgrind.sup
index fc6bfaac58..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
- Memcheck:Cond
- ...
- fun:glfwInit
- ...
-}
-{
- Ubuntu 16.04 - Qt + glib
- Memcheck:Cond
- fun:g_utf8_offset_to_pointer
- ...
-}
-{
- Ubuntu 16.04 - Qt + glib
+ Graphics driver bugs
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
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
...
}
{
- 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*
- ...
-}
-{
- Ubuntu 16.04 - Qt
- Memcheck:Leak
+ Graphics driver bugs
+ Memcheck:Value8
...
- fun:*QNetworkConfigurationManagerPrivate*updateConfigurations*
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
...
}
{
@@ -156,184 +33,46 @@
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
+ util::write_file
Memcheck:Param
write(buf)
...
obj:*/libc-*
- fun:_ZN4mbgl4util10write_fileERKSsS2_
- ...
-}
-{
- 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
+ fun:_ZN4mbgl4util10write_fileERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES8_
...
}
diff --git a/src/csscolorparser/csscolorparser.cpp b/src/csscolorparser/csscolorparser.cpp
index e4db8c7db9..4d1c6a3d65 100644
--- a/src/csscolorparser/csscolorparser.cpp
+++ b/src/csscolorparser/csscolorparser.cpp
@@ -110,9 +110,6 @@ const NamedColor namedColors[] = {
{ "yellow", { 255, 255, 0, 1 } }, { "yellowgreen", { 154, 205, 50, 1 } }
};
-const size_t namedColorCount = sizeof (namedColors) / sizeof (NamedColor);
-
-
template <typename T>
uint8_t clamp_css_byte(T i) { // Clamp to integer 0 .. 255.
i = ::round(i); // Seems to be what Chrome does (vs truncation).
@@ -189,9 +186,9 @@ optional<Color> parse(const std::string& css_str) {
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
- for (size_t i = 0; i < namedColorCount; i++) {
- if (str == namedColors[i].name) {
- return { namedColors[i].color };
+ for (const auto& namedColor : namedColors) {
+ if (str == namedColor.name) {
+ return { namedColor.color };
}
}
diff --git a/src/mbgl/actor/mailbox.cpp b/src/mbgl/actor/mailbox.cpp
index 5f60629833..373c24275f 100644
--- a/src/mbgl/actor/mailbox.cpp
+++ b/src/mbgl/actor/mailbox.cpp
@@ -10,8 +10,24 @@ Mailbox::Mailbox(Scheduler& scheduler_)
: scheduler(scheduler_) {
}
+void Mailbox::close() {
+ // Block until neither receive() nor push() are in progress. Two mutexes are used because receive()
+ // must not block send(). Of the two, the receiving mutex must be acquired first, because that is
+ // the order that an actor will obtain them when it self-sends a message, and consistent lock
+ // acquisition order prevents deadlocks.
+ // The receiving mutex is recursive to allow a mailbox (and thus the actor) to close itself.
+ std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex);
+ std::lock_guard<std::mutex> pushingLock(pushingMutex);
+
+ closed = true;
+}
+
void Mailbox::push(std::unique_ptr<Message> message) {
- assert(!closing);
+ std::lock_guard<std::mutex> pushingLock(pushingMutex);
+
+ if (closed) {
+ return;
+ }
std::lock_guard<std::mutex> queueLock(queueMutex);
bool wasEmpty = queue.empty();
@@ -21,16 +37,10 @@ void Mailbox::push(std::unique_ptr<Message> message) {
}
}
-void Mailbox::close() {
- // Block until the scheduler is guaranteed not to be executing receive().
- std::lock_guard<std::mutex> closingLock(closingMutex);
- closing = true;
-}
-
void Mailbox::receive() {
- std::lock_guard<std::mutex> closingLock(closingMutex);
+ std::lock_guard<std::recursive_mutex> receivingLock(receivingMutex);
- if (closing) {
+ if (closed) {
return;
}
diff --git a/src/mbgl/actor/message.hpp b/src/mbgl/actor/message.hpp
deleted file mode 100644
index cf071d4933..0000000000
--- a/src/mbgl/actor/message.hpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include <utility>
-
-namespace mbgl {
-
-// A movable type-erasing function wrapper. This allows to store arbitrary invokable
-// things (like std::function<>, or the result of a movable-only std::bind()) in the queue.
-// Source: http://stackoverflow.com/a/29642072/331379
-class Message {
-public:
- virtual ~Message() = default;
- virtual void operator()() = 0;
-};
-
-template <class Object, class MemberFn, class ArgsTuple>
-class MessageImpl : public Message {
-public:
- MessageImpl(Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
- : object(object_),
- memberFn(memberFn_),
- argsTuple(std::move(argsTuple_)) {
- }
-
- void operator()() override {
- invoke(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>());
- }
-
- template <std::size_t... I>
- void invoke(std::index_sequence<I...>) {
- (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
- }
-
- Object& object;
- MemberFn memberFn;
- ArgsTuple argsTuple;
-};
-
-namespace actor {
-
-template <class Object, class MemberFn, class... Args>
-std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&... args) {
- auto tuple = std::make_tuple(std::forward<Args>(args)...);
- return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple));
-}
-
-} // namespace actor
-} // namespace mbgl
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..db62214220 100644
--- a/src/mbgl/algorithm/generate_clip_ids_impl.hpp
+++ b/src/mbgl/algorithm/generate_clip_ids_impl.hpp
@@ -7,14 +7,16 @@
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;
+ auto& renderable = it->get();
if (!renderable.used) {
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..0c2266ff47 100644
--- a/src/mbgl/algorithm/update_renderables.hpp
+++ b/src/mbgl/algorithm/update_renderables.hpp
@@ -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);
@@ -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);
+ 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
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 88153f5fb7..1f2d01e9eb 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -4,8 +4,8 @@
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/annotation/line_annotation_impl.hpp>
#include <mbgl/annotation/fill_annotation_impl.hpp>
-#include <mbgl/annotation/style_sourced_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>
#include <mbgl/storage/file_source.hpp>
@@ -19,39 +19,42 @@ using namespace style;
const std::string AnnotationManager::SourceID = "com.mapbox.annotations";
const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points";
-AnnotationManager::AnnotationManager(float pixelRatio)
- : spriteAtlas({ 1024, 1024 }, pixelRatio) {
- // This is a special atlas, holding only images added via addIcon, so we always treat it as
- // loaded.
- spriteAtlas.markAsLoaded();
-}
+AnnotationManager::AnnotationManager(Style& style_)
+ : style(style_) {
+};
AnnotationManager::~AnnotationManager() = default;
+void AnnotationManager::setStyle(Style& style_) {
+ style = style_;
+}
+
+void AnnotationManager::onStyleLoaded() {
+ updateStyle();
+}
+
AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, const uint8_t maxZoom) {
+ std::lock_guard<std::mutex> lock(mutex);
AnnotationID id = nextID++;
Annotation::visit(annotation, [&] (const auto& annotation_) {
this->add(id, annotation_, maxZoom);
});
+ dirty = true;
return id;
}
-Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
- return Annotation::visit(annotation, [&] (const auto& annotation_) {
- return this->update(id, annotation_, maxZoom);
+bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
+ std::lock_guard<std::mutex> lock(mutex);
+ Annotation::visit(annotation, [&] (const auto& annotation_) {
+ this->update(id, annotation_, maxZoom);
});
+ return dirty;
}
void AnnotationManager::removeAnnotation(const AnnotationID& id) {
- if (symbolAnnotations.find(id) != symbolAnnotations.end()) {
- symbolTree.remove(symbolAnnotations.at(id));
- symbolAnnotations.erase(id);
- } else if (shapeAnnotations.find(id) != shapeAnnotations.end()) {
- obsoleteShapeAnnotationLayers.insert(shapeAnnotations.at(id)->layerID);
- shapeAnnotations.erase(id);
- } else {
- assert(false); // Should never happen
- }
+ std::lock_guard<std::mutex> lock(mutex);
+ remove(id);
+ dirty = true;
}
void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) {
@@ -63,82 +66,67 @@ void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& anno
void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
std::make_unique<LineAnnotationImpl>(id, annotation, maxZoom)).first->second;
- obsoleteShapeAnnotationLayers.erase(impl.layerID);
+ impl.updateStyle(*style.get().impl);
}
void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
std::make_unique<FillAnnotationImpl>(id, annotation, maxZoom)).first->second;
- obsoleteShapeAnnotationLayers.erase(impl.layerID);
-}
-
-void AnnotationManager::add(const AnnotationID& id, const StyleSourcedAnnotation& annotation, const uint8_t maxZoom) {
- ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
- std::make_unique<StyleSourcedAnnotationImpl>(id, annotation, maxZoom)).first->second;
- obsoleteShapeAnnotationLayers.erase(impl.layerID);
+ 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, const uint8_t maxZoom) {
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, maxZoom);
}
-
- return result;
}
-Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
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;
-}
-Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
- auto it = shapeAnnotations.find(id);
- if (it == shapeAnnotations.end()) {
- assert(false); // Attempt to update a non-existent shape annotation
- return Update::Nothing;
- }
- removeAndAdd(id, annotation, maxZoom);
- return Update::AnnotationData | Update::AnnotationStyle;
+ shapeAnnotations.erase(it);
+ add(id, annotation, maxZoom);
+ dirty = true;
}
-Update AnnotationManager::update(const AnnotationID& id, const StyleSourcedAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
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, maxZoom);
+ dirty = true;
}
-void AnnotationManager::removeAndAdd(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
- removeAnnotation(id);
- Annotation::visit(annotation, [&] (const auto& annotation_) {
- this->add(id, annotation_, maxZoom);
- });
+void AnnotationManager::remove(const AnnotationID& id) {
+ if (symbolAnnotations.find(id) != symbolAnnotations.end()) {
+ symbolTree.remove(symbolAnnotations.at(id));
+ symbolAnnotations.erase(id);
+ } else if (shapeAnnotations.find(id) != shapeAnnotations.end()) {
+ auto it = shapeAnnotations.find(id);
+ *style.get().impl->removeLayer(it->second->layerID);
+ shapeAnnotations.erase(it);
+ } else {
+ assert(false); // Should never happen
+ }
}
std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const CanonicalTileID& tileID) {
@@ -147,13 +135,13 @@ std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const Canonic
auto tileData = std::make_unique<AnnotationTileData>();
- AnnotationTileLayer& pointLayer = tileData->layers.emplace(PointLayerID, PointLayerID).first->second;
+ auto pointLayer = tileData->addLayer(PointLayerID);
LatLngBounds tileBounds(tileID);
symbolTree.query(boost::geometry::index::intersects(tileBounds),
boost::make_function_output_iterator([&](const auto& val){
- val->updateLayer(tileID, pointLayer);
+ val->updateLayer(tileID, *pointLayer);
}));
for (const auto& shape : shapeAnnotations) {
@@ -163,60 +151,87 @@ std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const Canonic
return tileData;
}
-void AnnotationManager::updateStyle(Style& style) {
- // Create annotation source, point layer, and point bucket
- if (!style.getSource(SourceID)) {
- style.addSource(std::make_unique<AnnotationSource>());
+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.get().impl->getSource(SourceID)) {
+ style.get().impl->addSource(std::make_unique<AnnotationSource>());
std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID, SourceID);
layer->setSourceLayer(PointLayerID);
- layer->setIconImage({"{sprite}"});
+ layer->setIconImage({SourceID + ".{sprite}"});
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& layer : obsoleteShapeAnnotationLayers) {
- if (style.getLayer(layer)) {
- style.removeLayer(layer);
- }
+ for (const auto& image : images) {
+ // Call addImage even for images we may have previously added, because we must support
+ // addAnnotationImage being used to update an existing image. Creating a new image is
+ // relatively cheap, as it copies only the Immutable reference. (We can't keep track
+ // 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.get().impl->addImage(std::make_unique<style::Image>(image.second));
}
-
- obsoleteShapeAnnotationLayers.clear();
}
void AnnotationManager::updateData() {
- for (auto& tile : tiles) {
- tile->setData(getTileData(tile->id.canonical));
+ std::lock_guard<std::mutex> lock(mutex);
+ if (dirty) {
+ for (auto& tile : tiles) {
+ tile->setData(getTileData(tile->id.canonical));
+ }
+ dirty = false;
}
}
void AnnotationManager::addTile(AnnotationTile& tile) {
+ std::lock_guard<std::mutex> lock(mutex);
tiles.insert(&tile);
tile.setData(getTileData(tile.id.canonical));
}
void AnnotationManager::removeTile(AnnotationTile& tile) {
+ std::lock_guard<std::mutex> lock(mutex);
tiles.erase(&tile);
}
-void AnnotationManager::addImage(const std::string& id, std::unique_ptr<style::Image> image) {
- spriteAtlas.addImage(id, std::move(image));
+// To ensure that annotation images do not collide with images from the style,
+// we prefix input image IDs with "com.mapbox.annotations".
+static std::string prefixedImageID(const std::string& id) {
+ return AnnotationManager::SourceID + "." + id;
+}
+
+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);
+ 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) {
- spriteAtlas.removeImage(id);
+void AnnotationManager::removeImage(const std::string& id_) {
+ std::lock_guard<std::mutex> lock(mutex);
+ const std::string id = prefixedImageID(id_);
+ images.erase(id);
+ style.get().impl->removeImage(id);
}
-double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id) {
- const style::Image* image = spriteAtlas.getImage(id);
- return image ? -(image->image.size.height / image->pixelRatio) / 2 : 0;
+double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id_) {
+ std::lock_guard<std::mutex> lock(mutex);
+ const std::string id = prefixedImageID(id_);
+ auto it = images.find(id);
+ return it != images.end() ? -(it->second.getImage().size.height / it->second.getPixelRatio()) / 2 : 0;
}
} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp
index 0ab43bec15..a028a8f1ba 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -2,13 +2,14 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/annotation/symbol_annotation_impl.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/map/update.hpp>
+#include <mbgl/style/image.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mutex>
#include <string>
#include <vector>
#include <unordered_set>
+#include <unordered_map>
namespace mbgl {
@@ -20,24 +21,24 @@ class ShapeAnnotationImpl;
namespace style {
class Style;
-class Image;
} // namespace style
class AnnotationManager : private util::noncopyable {
public:
- AnnotationManager(float pixelRatio);
+ AnnotationManager(style::Style&);
~AnnotationManager();
AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom);
- Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
+ bool updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
void removeAnnotation(const AnnotationID&);
- void addImage(const std::string&, std::unique_ptr<style::Image>);
+ void addImage(std::unique_ptr<style::Image>);
void removeImage(const std::string&);
double getTopOffsetPixelsForImage(const std::string&);
- SpriteAtlas& getSpriteAtlas() { return spriteAtlas; }
- void updateStyle(style::Style&);
+ void setStyle(style::Style&);
+ void onStyleLoaded();
+
void updateData();
void addTile(AnnotationTile&);
@@ -50,17 +51,23 @@ 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);
- void add(const AnnotationID&, const StyleSourcedAnnotation&, 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);
- Update update(const AnnotationID&, const StyleSourcedAnnotation&, const uint8_t);
+ void update(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
+ void update(const AnnotationID&, const LineAnnotation&, const uint8_t);
+ void update(const AnnotationID&, const FillAnnotation&, const uint8_t);
+
+ void remove(const AnnotationID&);
- void removeAndAdd(const AnnotationID&, const Annotation&, const uint8_t);
+ 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>>;
@@ -68,13 +75,14 @@ private:
// <https://github.com/mapbox/mapbox-gl-native/issues/5691>
using SymbolAnnotationMap = std::map<AnnotationID, std::shared_ptr<SymbolAnnotationImpl>>;
using ShapeAnnotationMap = std::map<AnnotationID, std::unique_ptr<ShapeAnnotationImpl>>;
+ using ImageMap = std::unordered_map<std::string, style::Image>;
SymbolAnnotationTree symbolTree;
SymbolAnnotationMap symbolAnnotations;
ShapeAnnotationMap shapeAnnotations;
- std::unordered_set<std::string> obsoleteShapeAnnotationLayers;
+ ImageMap images;
+
std::unordered_set<AnnotationTile*> tiles;
- SpriteAtlas spriteAtlas;
friend class AnnotationTile;
};
diff --git a/src/mbgl/annotation/annotation_source.cpp b/src/mbgl/annotation/annotation_source.cpp
index 9956140179..68f36f2d3a 100644
--- a/src/mbgl/annotation/annotation_source.cpp
+++ b/src/mbgl/annotation/annotation_source.cpp
@@ -1,25 +1,24 @@
#include <mbgl/annotation/annotation_source.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
-#include <mbgl/annotation/render_annotation_source.hpp>
namespace mbgl {
using namespace style;
AnnotationSource::AnnotationSource()
- : Source(SourceType::Annotations, std::make_unique<Impl>(*this)) {
+ : Source(makeMutable<Impl>()) {
}
-AnnotationSource::Impl::Impl(Source& base_)
- : Source::Impl(SourceType::Annotations, AnnotationManager::SourceID, base_) {
+AnnotationSource::Impl::Impl()
+ : Source::Impl(SourceType::Annotations, AnnotationManager::SourceID) {
}
-void AnnotationSource::Impl::loadDescription(FileSource&) {
+void AnnotationSource::loadDescription(FileSource&) {
loaded = true;
}
-std::unique_ptr<RenderSource> AnnotationSource::Impl::createRenderSource() const {
- return std::make_unique<RenderAnnotationSource>(*this);
+optional<std::string> AnnotationSource::Impl::getAttribution() const {
+ return {};
}
} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_source.hpp b/src/mbgl/annotation/annotation_source.hpp
index 46c9564443..0728f3207e 100644
--- a/src/mbgl/annotation/annotation_source.hpp
+++ b/src/mbgl/annotation/annotation_source.hpp
@@ -10,14 +10,19 @@ public:
AnnotationSource();
class Impl;
+ const Impl& impl() const;
+
+private:
+ void loadDescription(FileSource&) final;
+
+ Mutable<Impl> mutableImpl() const;
};
class AnnotationSource::Impl : public style::Source::Impl {
public:
- Impl(Source&);
+ Impl();
- void loadDescription(FileSource&) final;
- std::unique_ptr<RenderSource> createRenderSource() const final;
+ optional<std::string> getAttribution() const final;
};
} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp
index 1253681414..0596d60f4f 100644
--- a/src/mbgl/annotation/annotation_tile.cpp
+++ b/src/mbgl/annotation/annotation_tile.cpp
@@ -3,7 +3,6 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/style/style.hpp>
#include <utility>
@@ -11,9 +10,7 @@ namespace mbgl {
AnnotationTile::AnnotationTile(const OverscaledTileID& overscaledTileID,
const TileParameters& parameters)
- : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters,
- *parameters.style.glyphAtlas,
- parameters.annotationManager.spriteAtlas),
+ : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters),
annotationManager(parameters.annotationManager) {
annotationManager.addTile(*this);
}
@@ -22,37 +19,105 @@ AnnotationTile::~AnnotationTile() {
annotationManager.removeTile(*this);
}
-void AnnotationTile::setNecessity(Necessity) {}
+void AnnotationTile::setNecessity(Necessity) {
+}
+
+class AnnotationTileFeatureData {
+public:
+ AnnotationTileFeatureData(const AnnotationID id_,
+ FeatureType type_,
+ GeometryCollection&& geometries_,
+ std::unordered_map<std::string, std::string>&& properties_)
+ : id(id_),
+ type(type_),
+ geometries(std::move(geometries_)),
+ properties(std::move(properties_)) {
+ }
+
+ AnnotationID id;
+ FeatureType type;
+ GeometryCollection geometries;
+ std::unordered_map<std::string, std::string> properties;
+};
-AnnotationTileFeature::AnnotationTileFeature(const AnnotationID id_,
- FeatureType type_, GeometryCollection geometries_,
- std::unordered_map<std::string, std::string> properties_)
- : id(id_),
- type(type_),
- properties(std::move(properties_)),
- geometries(std::move(geometries_)) {}
+AnnotationTileFeature::AnnotationTileFeature(std::shared_ptr<const AnnotationTileFeatureData> data_)
+ : data(std::move(data_)) {
+}
+
+AnnotationTileFeature::~AnnotationTileFeature() = default;
+
+FeatureType AnnotationTileFeature::getType() const {
+ return data->type;
+}
optional<Value> AnnotationTileFeature::getValue(const std::string& key) const {
- auto it = properties.find(key);
- if (it != properties.end()) {
+ auto it = data->properties.find(key);
+ if (it != data->properties.end()) {
return optional<Value>(it->second);
}
return optional<Value>();
}
-AnnotationTileLayer::AnnotationTileLayer(std::string name_)
- : name(std::move(name_)) {}
+optional<FeatureIdentifier> AnnotationTileFeature::getID() const {
+ return { static_cast<uint64_t>(data->id) };
+}
+
+GeometryCollection AnnotationTileFeature::getGeometries() const {
+ return data->geometries;
+}
+
+class AnnotationTileLayerData {
+public:
+ AnnotationTileLayerData(const std::string& name_) : name(name_) {
+ }
+
+ const std::string name;
+ std::vector<std::shared_ptr<const AnnotationTileFeatureData>> features;
+};
+
+AnnotationTileLayer::AnnotationTileLayer(std::shared_ptr<AnnotationTileLayerData> layer_) : layer(std::move(layer_)) {
+}
+
+std::size_t AnnotationTileLayer::featureCount() const {
+ return layer->features.size();
+}
+
+std::unique_ptr<GeometryTileFeature> AnnotationTileLayer::getFeature(std::size_t i) const {
+ return std::make_unique<AnnotationTileFeature>(layer->features.at(i));
+}
+
+std::string AnnotationTileLayer::getName() const {
+ return layer->name;
+}
+
+void AnnotationTileLayer::addFeature(const AnnotationID id,
+ FeatureType type,
+ GeometryCollection geometries,
+ std::unordered_map<std::string, std::string> properties) {
+
+ layer->features.emplace_back(std::make_shared<AnnotationTileFeatureData>(
+ id, type, std::move(geometries), std::move(properties)));
+}
std::unique_ptr<GeometryTileData> AnnotationTileData::clone() const {
return std::make_unique<AnnotationTileData>(*this);
}
-const GeometryTileLayer* AnnotationTileData::getLayer(const std::string& name) const {
+std::unique_ptr<GeometryTileLayer> AnnotationTileData::getLayer(const std::string& name) const {
auto it = layers.find(name);
if (it != layers.end()) {
- return &it->second;
+ return std::make_unique<AnnotationTileLayer>(it->second);
}
return nullptr;
}
+std::unique_ptr<AnnotationTileLayer> AnnotationTileData::addLayer(const std::string& name) {
+ // Only constructs a new layer if it doesn't yet exist, otherwise, we'll use the existing one.
+ auto it = layers.find(name);
+ if (it == layers.end()) {
+ it = layers.emplace(name, std::make_shared<AnnotationTileLayerData>(name)).first;
+ }
+ return std::make_unique<AnnotationTileLayer>(it->second);
+}
+
} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp
index ea4ff5ebd5..88505c50e3 100644
--- a/src/mbgl/annotation/annotation_tile.hpp
+++ b/src/mbgl/annotation/annotation_tile.hpp
@@ -11,8 +11,7 @@ class TileParameters;
class AnnotationTile : public GeometryTile {
public:
- AnnotationTile(const OverscaledTileID&,
- const TileParameters&);
+ AnnotationTile(const OverscaledTileID&, const TileParameters&);
~AnnotationTile() override;
void setNecessity(Necessity) final;
@@ -21,46 +20,50 @@ private:
AnnotationManager& annotationManager;
};
+class AnnotationTileFeatureData;
+
class AnnotationTileFeature : public GeometryTileFeature {
public:
- AnnotationTileFeature(AnnotationID, FeatureType, GeometryCollection,
- std::unordered_map<std::string, std::string> properties = {{}});
+ AnnotationTileFeature(std::shared_ptr<const AnnotationTileFeatureData>);
+ ~AnnotationTileFeature() override;
- FeatureType getType() const override { return type; }
+ FeatureType getType() const override;
optional<Value> getValue(const std::string&) const override;
- optional<FeatureIdentifier> getID() const override { return { static_cast<uint64_t>(id) }; }
- GeometryCollection getGeometries() const override { return geometries; }
+ optional<FeatureIdentifier> getID() const override;
+ GeometryCollection getGeometries() const override;
- const AnnotationID id;
- const FeatureType type;
- const std::unordered_map<std::string, std::string> properties;
- const GeometryCollection geometries;
+private:
+ std::shared_ptr<const AnnotationTileFeatureData> data;
};
+class AnnotationTileLayerData;
+
class AnnotationTileLayer : public GeometryTileLayer {
public:
- AnnotationTileLayer(std::string);
-
- std::size_t featureCount() const override { return features.size(); }
+ AnnotationTileLayer(std::shared_ptr<AnnotationTileLayerData>);
- std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
- return std::make_unique<AnnotationTileFeature>(features.at(i));
- }
+ std::size_t featureCount() const override;
+ std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override;
+ std::string getName() const override;
- std::string getName() const override { return name; };
-
- std::vector<AnnotationTileFeature> features;
+ void addFeature(const AnnotationID,
+ FeatureType,
+ GeometryCollection,
+ std::unordered_map<std::string, std::string> properties = { {} });
private:
- std::string name;
+ std::shared_ptr<AnnotationTileLayerData> layer;
};
class AnnotationTileData : public GeometryTileData {
public:
std::unique_ptr<GeometryTileData> clone() const override;
- const GeometryTileLayer* getLayer(const std::string&) const override;
+ std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override;
+
+ std::unique_ptr<AnnotationTileLayer> addLayer(const std::string&);
- std::unordered_map<std::string, AnnotationTileLayer> layers;
+private:
+ std::unordered_map<std::string, std::shared_ptr<AnnotationTileLayerData>> layers;
};
} // namespace mbgl
diff --git a/src/mbgl/annotation/fill_annotation_impl.cpp b/src/mbgl/annotation/fill_annotation_impl.cpp
index 3e91524e86..9c73aeb796 100644
--- a/src/mbgl/annotation/fill_annotation_impl.cpp
+++ b/src/mbgl/annotation/fill_annotation_impl.cpp
@@ -1,6 +1,6 @@
#include <mbgl/annotation/fill_annotation_impl.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
-#include <mbgl/style/style.hpp>
+#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
namespace mbgl {
@@ -9,10 +9,10 @@ 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 }) {
+ annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor) {
}
-void FillAnnotationImpl::updateStyle(Style& style) const {
+void FillAnnotationImpl::updateStyle(Style::Impl& style) const {
Layer* layer = style.getLayer(layerID);
if (!layer) {
@@ -21,7 +21,7 @@ void FillAnnotationImpl::updateStyle(Style& style) const {
layer = style.addLayer(std::move(newLayer), AnnotationManager::PointLayerID);
}
- FillLayer* fillLayer = layer->as<FillLayer>();
+ auto* fillLayer = layer->as<FillLayer>();
fillLayer->setFillOpacity(annotation.opacity);
fillLayer->setFillColor(annotation.color);
fillLayer->setFillOutlineColor(annotation.outlineColor);
diff --git a/src/mbgl/annotation/fill_annotation_impl.hpp b/src/mbgl/annotation/fill_annotation_impl.hpp
index 6376eee880..5c49e447b8 100644
--- a/src/mbgl/annotation/fill_annotation_impl.hpp
+++ b/src/mbgl/annotation/fill_annotation_impl.hpp
@@ -9,7 +9,7 @@ class FillAnnotationImpl : public ShapeAnnotationImpl {
public:
FillAnnotationImpl(AnnotationID, FillAnnotation, uint8_t maxZoom);
- void updateStyle(style::Style&) const final;
+ void updateStyle(style::Style::Impl&) const final;
const ShapeAnnotationGeometry& geometry() const final;
private:
diff --git a/src/mbgl/annotation/line_annotation_impl.cpp b/src/mbgl/annotation/line_annotation_impl.cpp
index 15fa2c67f3..d35b956888 100644
--- a/src/mbgl/annotation/line_annotation_impl.cpp
+++ b/src/mbgl/annotation/line_annotation_impl.cpp
@@ -1,6 +1,6 @@
#include <mbgl/annotation/line_annotation_impl.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
-#include <mbgl/style/style.hpp>
+#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/layers/line_layer.hpp>
namespace mbgl {
@@ -9,10 +9,10 @@ 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 }) {
+ annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color) {
}
-void LineAnnotationImpl::updateStyle(Style& style) const {
+void LineAnnotationImpl::updateStyle(Style::Impl& style) const {
Layer* layer = style.getLayer(layerID);
if (!layer) {
@@ -22,7 +22,7 @@ void LineAnnotationImpl::updateStyle(Style& style) const {
layer = style.addLayer(std::move(newLayer), AnnotationManager::PointLayerID);
}
- LineLayer* lineLayer = layer->as<LineLayer>();
+ auto* lineLayer = layer->as<LineLayer>();
lineLayer->setLineOpacity(annotation.opacity);
lineLayer->setLineWidth(annotation.width);
lineLayer->setLineColor(annotation.color);
diff --git a/src/mbgl/annotation/line_annotation_impl.hpp b/src/mbgl/annotation/line_annotation_impl.hpp
index 7945da5d97..548a094d53 100644
--- a/src/mbgl/annotation/line_annotation_impl.hpp
+++ b/src/mbgl/annotation/line_annotation_impl.hpp
@@ -9,7 +9,7 @@ class LineAnnotationImpl : public ShapeAnnotationImpl {
public:
LineAnnotationImpl(AnnotationID, LineAnnotation, uint8_t maxZoom);
- void updateStyle(style::Style&) const final;
+ void updateStyle(style::Style::Impl&) const final;
const ShapeAnnotationGeometry& geometry() const final;
private:
diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp
index a62d2d51d3..34fb576727 100644
--- a/src/mbgl/annotation/render_annotation_source.cpp
+++ b/src/mbgl/annotation/render_annotation_source.cpp
@@ -1,6 +1,7 @@
#include <mbgl/annotation/render_annotation_source.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
@@ -9,65 +10,65 @@ namespace mbgl {
using namespace style;
-RenderAnnotationSource::RenderAnnotationSource(const AnnotationSource::Impl& impl_)
+RenderAnnotationSource::RenderAnnotationSource(Immutable<AnnotationSource::Impl> impl_)
: RenderSource(impl_) {
tilePyramid.setObserver(this);
}
+const AnnotationSource::Impl& RenderAnnotationSource::impl() const {
+ return static_cast<const AnnotationSource::Impl&>(*baseImpl);
+}
+
bool RenderAnnotationSource::isLoaded() const {
return tilePyramid.isLoaded();
}
-void RenderAnnotationSource::invalidateTiles() {
- tilePyramid.invalidateTiles();
+void RenderAnnotationSource::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;
+
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::Annotations,
+ util::tileSize,
+ { 0, util::DEFAULT_MAX_ZOOM },
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<AnnotationTile>(tileID, parameters);
+ });
}
-void RenderAnnotationSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) {
- generator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(projMatrix, clipMatrix, transform);
+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();
}
-void RenderAnnotationSource::updateTiles(const TileParameters& parameters) {
- tilePyramid.updateTiles(parameters,
- SourceType::Annotations,
- util::tileSize,
- { 0, 22 },
- [&] (const OverscaledTileID& tileID) {
- return std::make_unique<AnnotationTile>(tileID, parameters);
- });
-}
-
-void RenderAnnotationSource::removeTiles() {
- tilePyramid.removeTiles();
-}
-
-void RenderAnnotationSource::reloadTiles() {
- tilePyramid.reloadTiles();
-}
-
std::unordered_map<std::string, std::vector<Feature>>
RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry,
- const TransformState& transformState,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, options);
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
}
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 9ae9340477..9536b2e101 100644
--- a/src/mbgl/annotation/render_annotation_source.hpp
+++ b/src/mbgl/annotation/render_annotation_source.hpp
@@ -8,46 +8,42 @@ namespace mbgl {
class RenderAnnotationSource : public RenderSource {
public:
- RenderAnnotationSource(const AnnotationSource::Impl&);
+ RenderAnnotationSource(Immutable<AnnotationSource::Impl>);
bool isLoaded() const final;
- // Called when the camera has changed. May load new tiles, unload obsolete tiles, or
- // trigger re-placement of existing complete tiles.
- void updateTiles(const TileParameters&) final;
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
- // Removes all tiles (by putting them into the cache).
- void removeTiles() final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- // Remove all tiles and clear the cache.
- void invalidateTiles() final;
-
- // Request that all loaded tiles re-run the layout operation on the existing source
- // data with fresh style information.
- void reloadTiles() final;
-
- void startRender(algorithm::ClipIDGenerator&,
- const mat4& projMatrix,
- const mat4& clipMatrix,
- const TransformState&) final;
- void finishRender(Painter&) 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 std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
private:
+ const AnnotationSource::Impl& impl() const;
+
TilePyramid tilePyramid;
};
+template <>
+inline bool RenderSource::is<RenderAnnotationSource>() const {
+ return baseImpl->type == SourceType::Annotations;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp
index d3ddf62b9e..0c1a631ad8 100644
--- a/src/mbgl/annotation/shape_annotation_impl.cpp
+++ b/src/mbgl/annotation/shape_annotation_impl.cpp
@@ -38,7 +38,7 @@ void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, Annotati
if (shapeTile.features.empty())
return;
- AnnotationTileLayer& layer = data.layers.emplace(layerID, layerID).first->second;
+ auto layer = data.addLayer(layerID);
ToGeometryCollection toGeometryCollection;
ToFeatureType toFeatureType;
@@ -53,7 +53,7 @@ void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, Annotati
renderGeometry = fixupPolygons(renderGeometry);
}
- layer.features.emplace_back(id, featureType, renderGeometry);
+ layer->addFeature(id, featureType, renderGeometry);
}
}
diff --git a/src/mbgl/annotation/shape_annotation_impl.hpp b/src/mbgl/annotation/shape_annotation_impl.hpp
index 800b4ec313..caf2cff1a5 100644
--- a/src/mbgl/annotation/shape_annotation_impl.hpp
+++ b/src/mbgl/annotation/shape_annotation_impl.hpp
@@ -1,9 +1,11 @@
#pragma once
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/util/geometry.hpp>
+#include <mbgl/style/style.hpp>
#include <string>
#include <memory>
@@ -13,16 +15,12 @@ namespace mbgl {
class AnnotationTileData;
class CanonicalTileID;
-namespace style {
-class Style;
-} // namespace style
-
class ShapeAnnotationImpl {
public:
ShapeAnnotationImpl(const AnnotationID, const uint8_t maxZoom);
virtual ~ShapeAnnotationImpl() = default;
- virtual void updateStyle(style::Style&) const = 0;
+ virtual void updateStyle(style::Style::Impl&) const = 0;
virtual const ShapeAnnotationGeometry& geometry() const = 0;
void updateTileData(const CanonicalTileID&, AnnotationTileData&);
diff --git a/src/mbgl/annotation/style_sourced_annotation_impl.cpp b/src/mbgl/annotation/style_sourced_annotation_impl.cpp
deleted file mode 100644
index cb664cf15d..0000000000
--- a/src/mbgl/annotation/style_sourced_annotation_impl.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <mbgl/annotation/style_sourced_annotation_impl.hpp>
-#include <mbgl/annotation/annotation_manager.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/style/layer.hpp>
-#include <mbgl/style/layer_impl.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
-#include <mbgl/style/layers/fill_layer.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-StyleSourcedAnnotationImpl::StyleSourcedAnnotationImpl(AnnotationID id_, StyleSourcedAnnotation annotation_, uint8_t maxZoom_)
- : ShapeAnnotationImpl(id_, maxZoom_),
- annotation(std::move(annotation_)) {
-}
-
-void StyleSourcedAnnotationImpl::updateStyle(Style& style) const {
- if (style.getLayer(layerID))
- return;
-
- const Layer* sourceLayer = style.getLayer(annotation.layerID);
- if (!sourceLayer)
- return;
-
- if (sourceLayer->is<LineLayer>()) {
- std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, AnnotationManager::SourceID);
- layer->as<LineLayer>()->setSourceLayer(layerID);
- layer->as<LineLayer>()->setVisibility(VisibilityType::Visible);
- style.addLayer(std::move(layer), sourceLayer->getID());
- } else if (sourceLayer->is<FillLayer>()) {
- std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, AnnotationManager::SourceID);
- layer->as<FillLayer>()->setSourceLayer(layerID);
- layer->as<FillLayer>()->setVisibility(VisibilityType::Visible);
- style.addLayer(std::move(layer), sourceLayer->getID());
- }
-}
-
-const ShapeAnnotationGeometry& StyleSourcedAnnotationImpl::geometry() const {
- return annotation.geometry;
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/annotation/style_sourced_annotation_impl.hpp b/src/mbgl/annotation/style_sourced_annotation_impl.hpp
deleted file mode 100644
index 82b947302d..0000000000
--- a/src/mbgl/annotation/style_sourced_annotation_impl.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <mbgl/annotation/shape_annotation_impl.hpp>
-#include <mbgl/annotation/annotation.hpp>
-
-namespace mbgl {
-
-class StyleSourcedAnnotationImpl : public ShapeAnnotationImpl {
-public:
- StyleSourcedAnnotationImpl(AnnotationID, StyleSourcedAnnotation, uint8_t maxZoom);
-
- void updateStyle(style::Style&) const final;
- const ShapeAnnotationGeometry& geometry() const final;
-
-private:
- const StyleSourcedAnnotation annotation;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/annotation/symbol_annotation_impl.cpp b/src/mbgl/annotation/symbol_annotation_impl.cpp
index e5ae5f4b91..c95eb481b5 100644
--- a/src/mbgl/annotation/symbol_annotation_impl.cpp
+++ b/src/mbgl/annotation/symbol_annotation_impl.cpp
@@ -18,7 +18,7 @@ void SymbolAnnotationImpl::updateLayer(const CanonicalTileID& tileID, Annotation
LatLng latLng { annotation.geometry.y, annotation.geometry.x };
TileCoordinate coordinate = TileCoordinate::fromLatLng(0, latLng);
GeometryCoordinate tilePoint = TileCoordinate::toGeometryCoordinate(UnwrappedTileID(0, tileID), coordinate.p);
- layer.features.emplace_back(id, FeatureType::Point, GeometryCollection {{ {{ tilePoint }} }}, featureProperties);
+ layer.addFeature(id, FeatureType::Point, GeometryCollection {{ {{ tilePoint }} }}, featureProperties);
}
} // namespace mbgl
diff --git a/src/mbgl/annotation/symbol_annotation_impl.hpp b/src/mbgl/annotation/symbol_annotation_impl.hpp
index 2c263ab41e..e41ff85f33 100644
--- a/src/mbgl/annotation/symbol_annotation_impl.hpp
+++ b/src/mbgl/annotation/symbol_annotation_impl.hpp
@@ -29,10 +29,6 @@
#include <boost/geometry/index/rtree.hpp>
#pragma GCC diagnostic pop
-// Make Boost Geometry aware of our LatLng type
-BOOST_GEOMETRY_REGISTER_POINT_2D_CONST(mbgl::LatLng, double, boost::geometry::cs::cartesian, longitude(), latitude())
-BOOST_GEOMETRY_REGISTER_BOX(mbgl::LatLngBounds, mbgl::LatLng, southwest(), northeast())
-
namespace mbgl {
class AnnotationTileLayer;
@@ -50,9 +46,42 @@ public:
} // namespace mbgl
-// Tell Boost Geometry how to access a std::shared_ptr<mbgl::SymbolAnnotation> object.
namespace boost {
namespace geometry {
+
+// Make Boost Geometry aware of our LatLng type
+namespace traits {
+
+template<> struct tag<mbgl::LatLng> { using type = point_tag; };
+template<> struct dimension<mbgl::LatLng> : boost::mpl::int_<2> {};
+template<> struct coordinate_type<mbgl::LatLng> { using type = double; };
+template<> struct coordinate_system<mbgl::LatLng> { using type = boost::geometry::cs::cartesian; };
+
+template<> struct access<mbgl::LatLng, 0> { static inline double get(mbgl::LatLng const& p) { return p.longitude(); } };
+template<> struct access<mbgl::LatLng, 1> { static inline double get(mbgl::LatLng const& p) { return p.latitude(); } };
+
+template<> struct tag<mbgl::LatLngBounds> { using type = box_tag; };
+template<> struct point_type<mbgl::LatLngBounds> { using type = mbgl::LatLng; };
+
+template <size_t D>
+struct indexed_access<mbgl::LatLngBounds, min_corner, D>
+{
+ using ct = coordinate_type<mbgl::LatLng>::type;
+ static inline ct get(mbgl::LatLngBounds const& b) { return geometry::get<D>(b.southwest()); }
+ static inline void set(mbgl::LatLngBounds& b, ct const& value) { geometry::set<D>(b.southwest(), value); }
+};
+
+template <size_t D>
+struct indexed_access<mbgl::LatLngBounds, max_corner, D>
+{
+ using ct = coordinate_type<mbgl::LatLng>::type;
+ static inline ct get(mbgl::LatLngBounds const& b) { return geometry::get<D>(b.northeast()); }
+ static inline void set(mbgl::LatLngBounds& b, ct const& value) { geometry::set<D>(b.northeast(), value); }
+};
+
+} // namespace traits
+
+// Tell Boost Geometry how to access a std::shared_ptr<mbgl::SymbolAnnotation> object.
namespace index {
template <>
@@ -64,6 +93,7 @@ struct indexable<std::shared_ptr<const mbgl::SymbolAnnotationImpl>> {
}
};
-} // end namespace index
-} // end namespace geometry
-} // end namespace boost
+} // namespace index
+
+} // namespace geometry
+} // namespace boost
diff --git a/src/mbgl/geometry/anchor.hpp b/src/mbgl/geometry/anchor.hpp
index 3ed2b23e1b..b24d8d04e0 100644
--- a/src/mbgl/geometry/anchor.hpp
+++ b/src/mbgl/geometry/anchor.hpp
@@ -17,6 +17,6 @@ public:
: point(x_, y_), angle(angle_), scale(scale_), segment(segment_) {}
};
-typedef std::vector<Anchor> Anchors;
+using Anchors = std::vector<Anchor>;
} // namespace mbgl
diff --git a/src/mbgl/geometry/binpack.hpp b/src/mbgl/geometry/binpack.hpp
deleted file mode 100644
index b715cbc2be..0000000000
--- a/src/mbgl/geometry/binpack.hpp
+++ /dev/null
@@ -1,101 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/rect.hpp>
-#include <cstdint>
-#include <list>
-
-namespace mbgl {
-
-template <typename T>
-class BinPack : private util::noncopyable {
-public:
- BinPack(T width, T height)
- : free(1, Rect<uint16_t>{ 0, 0, width, height }) {}
-public:
- Rect<T> allocate(T width, T height) {
- // Find the smallest free rect angle
- auto smallest = free.end();
- for (auto it = free.begin(); it != free.end(); ++it) {
- const Rect<T>& ref = *it;
- const Rect<T>& rect = *smallest;
- if (width <= ref.w && height <= ref.h) {
- if (smallest == free.end() || (ref.y <= rect.y && ref.x <= rect.x)) {
- smallest = it;
- } else {
- // Our current "smallest" rect is already closer to 0/0.
- }
- } else {
- // The rect in the free list is not big enough.
- }
- }
-
- if (smallest == free.end()) {
- // There's no space left for this char.
- return Rect<uint16_t>{ 0, 0, 0, 0 };
- } else {
- Rect<T> rect = *smallest;
- free.erase(smallest);
-
- // Shorter/Longer Axis Split Rule (SAS)
- // http://clb.demon.fi/files/RectangleBinPack.pdf p. 15
- // Ignore the dimension of R and just split long the shorter dimension
- // See Also: http://www.cs.princeton.edu/~chazelle/pubs/blbinpacking.pdf
- if (rect.w < rect.h) {
- // split horizontally
- // +--+---+
- // |__|___| <-- b1
- // +------+ <-- b2
- if (rect.w > width) free.emplace_back(rect.x + width, rect.y, rect.w - width, height);
- if (rect.h > height) free.emplace_back(rect.x, rect.y + height, rect.w, rect.h - height);
- } else {
- // split vertically
- // +--+---+
- // |__| | <-- b1
- // +--|---+ <-- b2
- if (rect.w > width) free.emplace_back(rect.x + width, rect.y, rect.w - width, rect.h);
- if (rect.h > height) free.emplace_back(rect.x, rect.y + height, width, rect.h - height);
- }
-
- return Rect<uint16_t>{ rect.x, rect.y, width, height };
- }
- }
-
-
- void release(Rect<T> rect) {
- // Simple algorithm to recursively merge the newly released cell with its
- // neighbor. This doesn't merge more than two cells at a time, and fails
- // for complicated merges.
- for (auto it = free.begin(); it != free.end(); ++it) {
- Rect<T> ref = *it;
- if (ref.y == rect.y && ref.h == rect.h && ref.x + ref.w == rect.x) {
- ref.w += rect.w;
- }
- else if (ref.x == rect.x && ref.w == rect.w && ref.y + ref.h == rect.y) {
- ref.h += rect.h;
- }
- else if (rect.y == ref.y && rect.h == ref.h && rect.x + rect.w == ref.x) {
- ref.x = rect.x;
- ref.w += rect.w;
- }
- else if (rect.x == ref.x && rect.w == ref.w && rect.y + rect.h == ref.y) {
- ref.y = rect.y;
- ref.h += rect.h;
- } else {
- continue;
- }
-
- free.erase(it);
- release(ref);
- return;
-
- }
-
- free.emplace_back(rect);
- };
-
-private:
- std::list<Rect<T>> free;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp
index 8251e4d03a..1adb933e44 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/style/style.hpp>
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
+#include <mbgl/renderer/query.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/text/collision_tile.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>
@@ -32,19 +31,6 @@ void FeatureIndex::insert(const GeometryCollection& geometries,
}
}
-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 +39,6 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature&
return a.sortIndex < b.sortIndex;
}
-static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions,
- const style::Style& 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);
- if (bucket) {
- additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits);
- }
- };
-
- if (queryOptions.layerIDs) {
- for (const auto& layerID : *queryOptions.layerIDs) {
- 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,
@@ -92,13 +48,13 @@ void FeatureIndex::query(
const RenderedQueryOptions& queryOptions,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const style::Style& style,
+ const std::vector<const RenderLayer*>& layers,
const CollisionTile* collisionTile,
- const GeometryTile& tile) const {
+ 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);
@@ -113,7 +69,7 @@ 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, layers, bearing, pixelsToTileUnits);
}
// Query symbol features, if they've been placed.
@@ -124,7 +80,7 @@ void FeatureIndex::query(
std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale);
std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
for (const auto& symbolFeature : symbolFeatures) {
- addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits);
}
}
@@ -135,30 +91,39 @@ void FeatureIndex::addFeature(
const RenderedQueryOptions& options,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const style::Style& 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 f7aa0182e4..2ae7da33df 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -11,12 +11,8 @@
namespace mbgl {
-class GeometryTile;
class RenderedQueryOptions;
-
-namespace style {
-class Style;
-} // namespace style
+class RenderLayer;
class CollisionTile;
class CanonicalTileID;
@@ -45,9 +41,9 @@ public:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const style::Style&,
+ const std::vector<const RenderLayer*>&,
const CollisionTile*,
- const GeometryTile& tile) const;
+ const float additionalQueryRadius) const;
static optional<GeometryCoordinates> translateQueryGeometry(
const GeometryCoordinates& queryGeometry,
@@ -66,7 +62,7 @@ private:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const style::Style&,
+ 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 4dd102400d..bb5b2ddc34 100644
--- a/src/mbgl/gl/attribute.cpp
+++ b/src/mbgl/gl/attribute.cpp
@@ -4,9 +4,35 @@
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;
+}
+
+std::set<std::string> getActiveAttributes(ProgramID id) {
+ std::set<std::string> activeAttributes;
+
+ 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);
+
+ 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
diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp
index 2d1d2386eb..fa6c2ddeab 100644
--- a/src/mbgl/gl/attribute.hpp
+++ b/src/mbgl/gl/attribute.hpp
@@ -8,6 +8,7 @@
#include <cstddef>
#include <vector>
+#include <set>
#include <functional>
#include <string>
#include <array>
@@ -213,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 {
@@ -229,11 +231,20 @@ public:
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>
@@ -257,7 +268,7 @@ public:
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 Bindings offsetBindings(const Bindings& bindings, std::size_t vertexOffset) {
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index a530528da1..d0c538efb0 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,16 +60,40 @@ 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) {
- if (const char* extensions =
+ if (const auto* extensions =
reinterpret_cast<const char*>(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) {
auto fn = [&](
@@ -94,7 +142,7 @@ UniqueShader Context::createShader(ShaderType type, const std::string& source) {
UniqueShader result { MBGL_CHECK_ERROR(glCreateShader(static_cast<GLenum>(type))), { this } };
const GLchar* sources = source.data();
- const GLsizei lengths = static_cast<GLsizei>(source.length());
+ const auto lengths = static_cast<GLsizei>(source.length());
MBGL_CHECK_ERROR(glShaderSource(result, 1, &sources, &lengths));
MBGL_CHECK_ERROR(glCompileShader(result));
@@ -164,15 +212,20 @@ 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;
}
+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) {
BufferID id = 0;
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
@@ -278,11 +331,9 @@ std::unique_ptr<uint8_t[]> Context::readFramebuffer(const Size size, const Textu
const size_t stride = size.width * (format == TextureFormat::RGBA ? 4 : 1);
auto data = std::make_unique<uint8_t[]>(stride * size.height);
-#if not MBGL_USE_GLES2
// When reading data from the framebuffer, make sure that we are storing the values
// tightly packed into the buffer to avoid buffer overruns.
pixelStorePack = { 1 };
-#endif // MBGL_USE_GLES2
MBGL_CHECK_ERROR(glReadPixels(0, 0, size.width, size.height, static_cast<GLenum>(format),
GL_UNSIGNED_BYTE, data.get()));
@@ -408,6 +459,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));
@@ -419,6 +473,7 @@ Context::createFramebuffer(const Texture& color,
UniqueTexture
Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) {
auto obj = createTexture();
+ pixelStoreUnpack = { 1 };
updateTexture(obj, size, data, format, unit);
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures.
// We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus.
@@ -431,7 +486,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,
@@ -445,7 +500,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) {
@@ -476,7 +531,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;
}
}
@@ -488,8 +543,8 @@ void Context::reset() {
}
void Context::setDirtyState() {
- // Note: does not set viewport/bindFramebuffer to dirty since they are handled separately in
- // the view object.
+ // Note: does not set viewport/scissorTest/bindFramebuffer to dirty
+ // since they are handled separately in the view object.
stencilFunc.setDirty();
stencilMask.setDirty();
stencilTest.setDirty();
@@ -508,13 +563,13 @@ void Context::setDirtyState() {
clearStencil.setDirty();
program.setDirty();
lineWidth.setDirty();
- activeTexture.setDirty();
+ activeTextureUnit.setDirty();
+ pixelStorePack.setDirty();
+ pixelStoreUnpack.setDirty();
#if not MBGL_USE_GLES2
pointSize.setDirty();
pixelZoom.setDirty();
rasterPos.setDirty();
- pixelStorePack.setDirty();
- pixelStoreUnpack.setDirty();
pixelTransferDepth.setDirty();
pixelTransferStencil.setDirty();
#endif // MBGL_USE_GLES2
@@ -659,8 +714,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()));
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 8929d24e54..528113cbba 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -25,9 +25,6 @@
#include <string>
namespace mbgl {
-
-class View;
-
namespace gl {
constexpr size_t TextureMax = 64;
@@ -64,13 +61,19 @@ 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) {
return IndexBuffer<DrawMode> {
@@ -187,7 +190,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
@@ -195,9 +204,10 @@ 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::Program> program;
State<value::BindVertexBuffer> vertexBuffer;
@@ -205,11 +215,12 @@ public:
State<value::BindVertexArray, const Context&> bindVertexArray { *this };
VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this };
+ State<value::PixelStorePack> pixelStorePack;
+ State<value::PixelStoreUnpack> pixelStoreUnpack;
+
#if not MBGL_USE_GLES2
State<value::PixelZoom> pixelZoom;
State<value::RasterPos> rasterPos;
- State<value::PixelStorePack> pixelStorePack;
- State<value::PixelStoreUnpack> pixelStoreUnpack;
State<value::PixelTransferDepth> pixelTransferDepth;
State<value::PixelTransferStencil> pixelTransferStencil;
#endif // MBGL_USE_GLES2
@@ -237,7 +248,8 @@ private:
State<value::PointSize> pointSize;
#endif // MBGL_USE_GLES2
- UniqueBuffer createVertexBuffer(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);
UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit);
void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit);
diff --git a/src/mbgl/gl/debugging.cpp b/src/mbgl/gl/debugging.cpp
index 0d69d58be5..366b4d63c7 100644
--- a/src/mbgl/gl/debugging.cpp
+++ b/src/mbgl/gl/debugging.cpp
@@ -10,9 +10,9 @@ namespace gl {
DebugGroup::DebugGroup(const Context& context_, const std::string& name) : context(context_) {
if (auto debugging = context.getDebuggingExtension()) {
if (debugging->pushDebugGroup) {
- debugging->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(name.size()), name.c_str());
+ MBGL_CHECK_ERROR(debugging->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(name.size()), name.c_str()));
} else if (debugging->pushGroupMarkerEXT) {
- debugging->pushGroupMarkerEXT(GLsizei(name.size() + 1), name.c_str());
+ MBGL_CHECK_ERROR(debugging->pushGroupMarkerEXT(GLsizei(name.size() + 1), name.c_str()));
}
}
}
@@ -20,9 +20,9 @@ DebugGroup::DebugGroup(const Context& context_, const std::string& name) : conte
DebugGroup::~DebugGroup() {
if (auto debugging = context.getDebuggingExtension()) {
if (debugging->popDebugGroup) {
- debugging->popDebugGroup();
+ MBGL_CHECK_ERROR(debugging->popDebugGroup());
} else if (debugging->popGroupMarkerEXT) {
- debugging->popGroupMarkerEXT();
+ MBGL_CHECK_ERROR(debugging->popGroupMarkerEXT());
}
}
}
diff --git a/src/mbgl/gl/debugging_extension.hpp b/src/mbgl/gl/debugging_extension.hpp
index c1835cfcdd..5657bbde88 100644
--- a/src/mbgl/gl/debugging_extension.hpp
+++ b/src/mbgl/gl/debugging_extension.hpp
@@ -53,13 +53,13 @@ namespace extension {
class Debugging {
public:
- typedef void (*Callback)(GLenum source,
- GLenum type,
- GLuint id,
- GLenum severity,
- GLsizei length,
- const GLchar* message,
- const void* userParam);
+ using Callback = void (*)(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const void* userParam);
static void DebugCallback(GLenum source,
GLenum type,
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/index_buffer.hpp b/src/mbgl/gl/index_buffer.hpp
index b3610f4154..01b6396e00 100644
--- a/src/mbgl/gl/index_buffer.hpp
+++ b/src/mbgl/gl/index_buffer.hpp
@@ -24,7 +24,9 @@ public:
std::size_t byteSize() const { return v.size() * sizeof(uint16_t); }
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;
diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp
index 1a429c6630..3b54ec194a 100644
--- a/src/mbgl/gl/program.hpp
+++ b/src/mbgl/gl/program.hpp
@@ -34,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,
@@ -140,8 +142,8 @@ public:
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/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 e7d2ca449a..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;
+// 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 {
@@ -66,8 +74,6 @@ enum class PrimitiveType {
TriangleFan = 0x0006
};
-#if not MBGL_USE_GLES2
-
struct PixelStorageType {
int32_t alignment;
};
@@ -76,9 +82,33 @@ constexpr bool operator!=(const PixelStorageType& a, const PixelStorageType& b)
return a.alignment != b.alignment;
}
-#endif // MBGL_USE_GLES2
-
using BinaryProgramFormat = uint32_t;
+enum class UniformDataType : uint32_t {
+ Float = 0x1406,
+ FloatVec2 = 0x8B50,
+ FloatVec3 = 0x8B51,
+ FloatVec4 = 0x8B52,
+ Int = 0x1404,
+ IntVec2 = 0x8B53,
+ IntVec3 = 0x8B54,
+ IntVec4 = 0x8B55,
+ Bool = 0x8B56,
+ BoolVec2 = 0x8B57,
+ BoolVec3 = 0x8B58,
+ BoolVec4 = 0x8B59,
+ FloatMat2 = 0x8B5A,
+ FloatMat3 = 0x8B5B,
+ FloatMat4 = 0x8B5C,
+ Sampler2D = 0x8B5E,
+ SamplerCube = 0x8B60,
+};
+
+enum class BufferUsage : uint32_t {
+ StreamDraw = 0x88E0,
+ StaticDraw = 0x88E4,
+ DynamicDraw = 0x88E8,
+};
+
} // namespace gl
} // namespace mbgl
diff --git a/src/mbgl/gl/uniform.cpp b/src/mbgl/gl/uniform.cpp
index 7b674f2cde..2946980c19 100644
--- a/src/mbgl/gl/uniform.cpp
+++ b/src/mbgl/gl/uniform.cpp
@@ -4,6 +4,8 @@
#include <mbgl/util/size.hpp>
#include <mbgl/util/convert.hpp>
+#include <memory>
+
namespace mbgl {
namespace gl {
@@ -79,5 +81,96 @@ void bindUniform<std::array<uint16_t, 2>>(UniformLocation location, const std::a
// Add more as needed.
+#ifndef NDEBUG
+
+ActiveUniforms activeUniforms(ProgramID id) {
+ ActiveUniforms active;
+
+ GLint count;
+ GLint maxLength;
+ MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &count));
+ MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength));
+
+ auto name = std::make_unique<GLchar[]>(maxLength);
+ GLsizei length;
+ GLint size;
+ GLenum type;
+ for (GLint index = 0; index < count; index++) {
+ MBGL_CHECK_ERROR(
+ glGetActiveUniform(id, index, maxLength, &length, &size, &type, name.get()));
+ active.emplace(
+ std::string{ name.get(), static_cast<size_t>(length) },
+ ActiveUniform{ static_cast<size_t>(size), static_cast<UniformDataType>(type) });
+ }
+
+ return active;
+}
+
+template <>
+bool verifyUniform<float>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 && uniform.type == UniformDataType::Float);
+ return true;
+}
+
+template <>
+bool verifyUniform<std::array<float, 2>>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2);
+ return true;
+}
+
+template <>
+bool verifyUniform<std::array<float, 3>>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec3);
+ return true;
+}
+
+template <>
+bool verifyUniform<std::array<double, 16>>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 && uniform.type == UniformDataType::FloatMat4);
+ return true;
+}
+
+template <>
+bool verifyUniform<bool>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 &&
+ (uniform.type == UniformDataType::Bool ||
+ uniform.type == UniformDataType::Int ||
+ uniform.type == UniformDataType::Float));
+ return true;
+}
+
+template <>
+bool verifyUniform<uint8_t>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 &&
+ (uniform.type == UniformDataType::Int ||
+ uniform.type == UniformDataType::Float ||
+ uniform.type == UniformDataType::Sampler2D));
+ return true;
+}
+
+template <>
+bool verifyUniform<Color>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec4);
+ return true;
+}
+
+template <>
+bool verifyUniform<Size>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 && uniform.type == UniformDataType::FloatVec2);
+ return true;
+}
+
+template <>
+bool verifyUniform<std::array<uint16_t, 2>>(const ActiveUniform& uniform) {
+ assert(uniform.size == 1 &&
+ (uniform.type == UniformDataType::IntVec2 ||
+ uniform.type == UniformDataType::FloatVec2));
+ return true;
+}
+
+// Add more as needed.
+
+#endif
+
} // namespace gl
} // namespace mbgl
diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp
index 3cac09c526..5a78068fc8 100644
--- a/src/mbgl/gl/uniform.hpp
+++ b/src/mbgl/gl/uniform.hpp
@@ -7,6 +7,7 @@
#include <array>
#include <vector>
+#include <map>
#include <functional>
namespace mbgl {
@@ -22,13 +23,33 @@ public:
T t;
};
+class ActiveUniform {
+public:
+ std::size_t size;
+ UniformDataType type;
+};
+
+#ifndef NDEBUG
+
+template <class T>
+bool verifyUniform(const ActiveUniform&);
+
+using ActiveUniforms = std::map<std::string, ActiveUniform>;
+ActiveUniforms activeUniforms(ProgramID);
+
+#endif
+
template <class Tag, class T>
class Uniform {
public:
using Value = UniformValue<Tag, T>;
+ using Type = T;
+
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;
@@ -70,12 +91,24 @@ public:
using NamedLocations = std::vector<std::pair<const std::string, UniformLocation>>;
static State bindLocations(const ProgramID& id) {
+#ifndef NDEBUG
+ // Verify active uniform types match the enum
+ const auto active = activeUniforms(id);
+
+ util::ignore(
+ { // Some shader programs have uniforms declared, but not used, so they're not active.
+ // Therefore, we'll only verify them when they are indeed active.
+ (active.find(Us::name()) != active.end()
+ ? verifyUniform<typename Us::Type>(active.at(Us::name()))
+ : false)... });
+#endif
+
return State { { uniformLocation(id, Us::name()) }... };
}
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) {
diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp
index 10818e8499..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);
@@ -267,6 +267,18 @@ Viewport::Type Viewport::Get() {
{ static_cast<uint32_t>(viewport[2]), static_cast<uint32_t>(viewport[3]) } };
}
+const constexpr ScissorTest::Type ScissorTest::Default;
+
+void ScissorTest::Set(const Type& value) {
+ MBGL_CHECK_ERROR(value ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST));
+}
+
+ScissorTest::Type ScissorTest::Get() {
+ Type scissorTest;
+ MBGL_CHECK_ERROR(scissorTest = glIsEnabled(GL_SCISSOR_TEST));
+ return scissorTest;
+}
+
const constexpr BindFramebuffer::Type BindFramebuffer::Default;
void BindFramebuffer::Set(const Type& value) {
@@ -371,6 +383,34 @@ void VertexAttribute::Set(const optional<AttributeBinding>& binding, Context& co
}
}
+const constexpr PixelStorePack::Type PixelStorePack::Default;
+
+void PixelStorePack::Set(const Type& value) {
+ assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 ||
+ value.alignment == 8);
+ MBGL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, value.alignment));
+}
+
+PixelStorePack::Type PixelStorePack::Get() {
+ Type value;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_PACK_ALIGNMENT, &value.alignment));
+ return value;
+}
+
+const constexpr PixelStoreUnpack::Type PixelStoreUnpack::Default;
+
+void PixelStoreUnpack::Set(const Type& value) {
+ assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 ||
+ value.alignment == 8);
+ MBGL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, value.alignment));
+}
+
+PixelStoreUnpack::Type PixelStoreUnpack::Get() {
+ Type value;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_UNPACK_ALIGNMENT, &value.alignment));
+ return value;
+}
+
#if not MBGL_USE_GLES2
const constexpr PointSize::Type PointSize::Default;
@@ -410,34 +450,6 @@ RasterPos::Type RasterPos::Get() {
return { pos[0], pos[1], pos[2], pos[3] };
}
-const constexpr PixelStorePack::Type PixelStorePack::Default;
-
-void PixelStorePack::Set(const Type& value) {
- assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 ||
- value.alignment == 8);
- MBGL_CHECK_ERROR(glPixelStorei(GL_PACK_ALIGNMENT, value.alignment));
-}
-
-PixelStorePack::Type PixelStorePack::Get() {
- Type value;
- MBGL_CHECK_ERROR(glGetIntegerv(GL_PACK_ALIGNMENT, &value.alignment));
- return value;
-}
-
-const constexpr PixelStoreUnpack::Type PixelStoreUnpack::Default;
-
-void PixelStoreUnpack::Set(const Type& value) {
- assert(value.alignment == 1 || value.alignment == 2 || value.alignment == 4 ||
- value.alignment == 8);
- MBGL_CHECK_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, value.alignment));
-}
-
-PixelStoreUnpack::Type PixelStoreUnpack::Get() {
- Type value;
- MBGL_CHECK_ERROR(glGetIntegerv(GL_UNPACK_ALIGNMENT, &value.alignment));
- return value;
-}
-
const constexpr PixelTransferDepth::Type PixelTransferDepth::Default;
void PixelTransferDepth::Set(const Type& value) {
diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp
index 62fe88a2f4..7b85a5ff4b 100644
--- a/src/mbgl/gl/value.hpp
+++ b/src/mbgl/gl/value.hpp
@@ -165,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&);
@@ -183,6 +183,13 @@ struct Viewport {
static Type Get();
};
+struct ScissorTest {
+ using Type = bool;
+ static const constexpr Type Default = false;
+ static void Set(const Type&);
+ static Type Get();
+};
+
constexpr bool operator!=(const Viewport::Type& a, const Viewport::Type& b) {
return a.x != b.x || a.y != b.y || a.size != b.size;
}
@@ -239,6 +246,20 @@ struct VertexAttribute {
static void Set(const Type&, Context&, AttributeLocation);
};
+struct PixelStorePack {
+ using Type = PixelStorageType;
+ static const constexpr Type Default = { 4 };
+ static void Set(const Type&);
+ static Type Get();
+};
+
+struct PixelStoreUnpack {
+ using Type = PixelStorageType;
+ static const constexpr Type Default = { 4 };
+ static void Set(const Type&);
+ static Type Get();
+};
+
#if not MBGL_USE_GLES2
struct PointSize {
@@ -278,20 +299,6 @@ constexpr bool operator!=(const RasterPos::Type& a, const RasterPos::Type& b) {
return a.x != b.x || a.y != b.y || a.z != b.z || a.w != b.w;
}
-struct PixelStorePack {
- using Type = PixelStorageType;
- static const constexpr Type Default = { 4 };
- static void Set(const Type&);
- static Type Get();
-};
-
-struct PixelStoreUnpack {
- using Type = PixelStorageType;
- static const constexpr Type Default = { 4 };
- static void Set(const Type&);
- static Type Get();
-};
-
struct PixelTransferDepth {
struct Type {
float scale;
diff --git a/src/mbgl/gl/vertex_buffer.hpp b/src/mbgl/gl/vertex_buffer.hpp
index c9bc01f3e8..9f8b156b25 100644
--- a/src/mbgl/gl/vertex_buffer.hpp
+++ b/src/mbgl/gl/vertex_buffer.hpp
@@ -26,7 +26,9 @@ public:
std::size_t byteSize() const { return v.size() * sizeof(Vertex); }
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 8816f4c95c..02fb800df6 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -5,8 +5,8 @@ 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,
@@ -16,33 +16,38 @@ SymbolInstance::SymbolInstance(Anchor& anchor,
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 GlyphPositions& face,
+ const std::array<float, 2> iconOffset_,
+ const GlyphPositionMap& positions,
const IndexedSubfeature& indexedFeature,
const std::size_t featureIndex_) :
- point(anchor.point),
+ 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.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature),
+ iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature),
+ featureIndex(featureIndex_),
+ textOffset(textOffset_),
+ iconOffset(iconOffset_) {
// Create the quads used for rendering the icon and glyphs.
if (addToBuffers) {
if (shapedIcon) {
- iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first);
+ iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
}
if (shapedTextOrientations.first) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, face);
+ auto quads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
}
if (shapedTextOrientations.second) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, face);
+ auto quads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
}
}
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index eadbf67475..f1df416cd1 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/text/quads.hpp>
-#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/text/collision_feature.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
@@ -13,7 +13,7 @@ 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&,
@@ -23,14 +23,17 @@ public:
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 GlyphPositions& face,
+ const std::array<float, 2> iconOffset,
+ const GlyphPositionMap&,
const IndexedSubfeature&,
const std::size_t featureIndex);
- Point<float> point;
+ Anchor anchor;
+ GeometryCoordinates line;
uint32_t index;
bool hasText;
bool hasIcon;
@@ -40,6 +43,8 @@ public:
CollisionFeature iconCollisionFeature;
WritingModeType writingModes;
std::size_t featureIndex;
+ std::array<float, 2> textOffset;
+ std::array<float, 2> iconOffset;
};
} // namespace mbgl
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 2accac281b..2c90b69b08 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -1,12 +1,12 @@
#include <mbgl/layout/symbol_layout.hpp>
#include <mbgl/layout/merge_lines.hpp>
#include <mbgl/layout/clip_lines.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/text/get_anchors.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/text/shaping.hpp>
@@ -40,21 +40,22 @@ static bool has(const style::SymbolLayoutProperties::PossiblyEvaluated& layout)
SymbolLayout::SymbolLayout(const BucketParameters& parameters,
const std::vector<const RenderLayer*>& layers,
- const GeometryTileLayer& sourceLayer,
- IconDependencies& iconDependencies,
+ std::unique_ptr<GeometryTileLayer> sourceLayer_,
+ ImageDependencies& imageDependencies,
GlyphDependencies& glyphDependencies)
- : sourceLayerName(sourceLayer.getName()),
+ : sourceLayer(std::move(sourceLayer_)),
bucketName(layers.at(0)->getID()),
overscaling(parameters.tileID.overscaleFactor()),
zoom(parameters.tileID.overscaledZ),
mode(parameters.mode),
+ pixelRatio(parameters.pixelRatio),
tileSize(util::tileSize * overscaling),
tilePixelRatio(float(util::EXTENT) / tileSize),
- textSize(layers.at(0)->as<RenderSymbolLayer>()->impl->layout.unevaluated.get<TextSize>()),
- iconSize(layers.at(0)->as<RenderSymbolLayer>()->impl->layout.unevaluated.get<IconSize>())
+ textSize(layers.at(0)->as<RenderSymbolLayer>()->impl().layout.get<TextSize>()),
+ iconSize(layers.at(0)->as<RenderSymbolLayer>()->impl().layout.get<IconSize>())
{
- const SymbolLayer::Impl& leader = *layers.at(0)->as<RenderSymbolLayer>()->impl;
+ const SymbolLayer::Impl& leader = layers.at(0)->as<RenderSymbolLayer>()->impl();
layout = leader.layout.evaluate(PropertyEvaluationParameters(zoom));
@@ -74,10 +75,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);
@@ -94,9 +98,9 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
}
// Determine glyph dependencies
- const size_t featureCount = sourceLayer.featureCount();
+ const size_t featureCount = sourceLayer->featureCount();
for (size_t i = 0; i < featureCount; ++i) {
- auto feature = sourceLayer.getFeature(i);
+ auto feature = sourceLayer->getFeature(i);
if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
continue;
@@ -136,12 +140,17 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
}
ft.text = applyArabicShaping(util::utf8_to_utf16::convert(u8string));
+ const bool canVerticalizeText = layout.get<TextRotationAlignment>() == AlignmentType::Map
+ && layout.get<SymbolPlacement>() == SymbolPlacementType::Line
+ && util::i18n::allowsVerticalWritingMode(*ft.text);
// Loop through all characters of this text and collect unique codepoints.
for (char16_t chr : *ft.text) {
glyphDependencies[layout.get<TextFont>()].insert(chr);
- if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) {
- glyphDependencies[layout.get<TextFont>()].insert(verticalChr);
+ if (canVerticalizeText) {
+ if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) {
+ glyphDependencies[layout.get<TextFont>()].insert(verticalChr);
+ }
}
}
}
@@ -152,7 +161,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
icon = util::replaceTokens(icon, getValue);
}
ft.icon = icon;
- iconDependencies.insert(*ft.icon);
+ imageDependencies.insert(*ft.icon);
}
if (ft.text || ft.icon) {
@@ -169,103 +178,67 @@ bool SymbolLayout::hasSymbolInstances() const {
return !symbolInstances.empty();
}
-void SymbolLayout::prepare(const GlyphPositionMap& glyphs, const IconMap& icons) {
- 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;
-
-
+void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
+ const ImageMap& imageMap, const ImagePositions& imagePositions) {
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
+ auto glyphMapIt = glyphMap.find(layout.get<TextFont>());
+ const Glyphs& glyphs = glyphMapIt != glyphMap.end()
+ ? glyphMapIt->second : Glyphs();
+
+ auto glyphPositionsIt = glyphPositions.find(layout.get<TextFont>());
+ const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end()
+ ? glyphPositionsIt->second : GlyphPositionMap();
+
for (auto it = features.begin(); it != features.end(); ++it) {
auto& feature = *it;
if (feature.geometry.empty()) continue;
std::pair<Shaping, Shaping> shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
- GlyphPositions face;
// if feature has text, shape the text
if (feature.text) {
- auto glyphPositions = glyphs.find(layout.get<TextFont>());
- if (glyphPositions != glyphs.end()) { // If there are no glyphs available for this feature, skip shaping
- auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) {
- const float oneEm = 24.0f;
- const Shaping result = getShaping(
- /* string */ text,
- /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ?
- layout.get<TextMaxWidth>() * 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,
- /* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
- /* verticalHeight */ oneEm,
- /* writingMode */ writingMode,
- /* bidirectional algorithm object */ bidi,
- /* glyphs */ glyphPositions->second);
-
- return result;
- };
-
- shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal);
-
- if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) {
- shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical);
- }
+ auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) {
+ const float oneEm = 24.0f;
+ const Shaping result = getShaping(
+ /* string */ text,
+ /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ?
+ layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0,
+ /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
+ /* 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,
+ /* bidirectional algorithm object */ bidi,
+ /* glyphs */ glyphs);
+
+ return result;
+ };
+
+ shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal);
+
+ if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) {
+ shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical);
}
}
// if feature has icon, get sprite atlas position
if (feature.icon) {
- auto image = icons.find(*feature.icon);
- if (image != icons.end()) {
- shapedIcon = PositionedIcon::shapeIcon(image->second,
+ auto image = imageMap.find(*feature.icon);
+ if (image != imageMap.end()) {
+ 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) {
+ if (image->second->sdf) {
sdfIcons = true;
}
- if (image->second.relativePixelRatio != 1.0f) {
+ if (image->second->pixelRatio != pixelRatio) {
iconsNeedLinear = true;
} else if (layout.get<IconRotate>().constantOr(1) != 0) {
iconsNeedLinear = true;
@@ -275,8 +248,7 @@ void SymbolLayout::prepare(const GlyphPositionMap& glyphs, const IconMap& icons)
// if either shapedText or icon position is present, add the feature
if (shapedTextOrientations.first || shapedIcon) {
- auto glyphPositionsIt = glyphs.find(layout.get<TextFont>());
- addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionsIt == glyphs.end() ? GlyphPositions() : glyphPositionsIt->second);
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap);
}
feature.geometry.clear();
@@ -289,12 +261,14 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolFeature& feature,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositions& glyphs) {
+ const GlyphPositionMap& glyphPositionMap) {
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.
@@ -318,8 +292,9 @@ void SymbolLayout::addFeature(const std::size_t index,
? SymbolPlacementType::Point
: layout.get<SymbolPlacement>();
const float textRepeatDistance = symbolSpacing / 2;
- IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, bucketName, symbolInstances.size()};
-
+ IndexedSubfeature indexedFeature = { feature.index, sourceLayer->getName(), bucketName,
+ symbolInstances.size() };
+
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
// +-------------------+ Symbols with anchors located on tile edges
@@ -344,9 +319,9 @@ void SymbolLayout::addFeature(const std::size_t index,
symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
layout.evaluate(zoom, feature), layoutTextSize,
addToBuffers, symbolInstances.size(),
- textBoxScale, textPadding, textPlacement,
- iconBoxScale, iconPadding, iconPlacement,
- glyphs, indexedFeature, index);
+ textBoxScale, textPadding, textPlacement, textOffset,
+ iconBoxScale, iconPadding, iconPlacement, iconOffset,
+ glyphPositionMap, indexedFeature, index);
};
const auto& type = feature.getType();
@@ -444,8 +419,8 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
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;
+ 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;
@@ -490,10 +465,21 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f);
collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>());
if (glyphScale < collisionTile.maxScale) {
+
+ const float labelAngle = std::fmod((symbolInstance.anchor.angle + collisionTile.config.angle) + 2 * M_PI, 2 * M_PI);
+ const bool inVerticalRange = (
+ (labelAngle > M_PI * 1.0 / 4.0 && labelAngle <= M_PI * 3.0 / 4) ||
+ (labelAngle > M_PI * 5.0 / 4.0 && labelAngle <= M_PI * 7.0 / 4));
+ const bool useVerticalMode = symbolInstance.writingModes & WritingModeType::Vertical && inVerticalRange;
+
+ const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature);
+ bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.textOffset, placementZoom, useVerticalMode, symbolInstance.line);
+
for (const auto& symbol : symbolInstance.glyphQuads) {
addSymbol(
- bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom,
- keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes);
+ bucket->text, sizeData, symbol, placementZoom,
+ keepUpright, textPlacement, symbolInstance.anchor, bucket->text.placedSymbols.back());
}
}
}
@@ -502,9 +488,12 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
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) {
+ const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
+ bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.iconOffset, placementZoom, false, symbolInstance.line);
addSymbol(
- bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom,
- keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes);
+ bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom,
+ keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back());
}
}
@@ -523,14 +512,13 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
template <typename Buffer>
void SymbolLayout::addSymbol(Buffer& buffer,
- SymbolSizeBinder& sizeBinder,
+ 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;
@@ -539,30 +527,9 @@ 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 (placement == style::SymbolPlacementType::Line && keepUpright) {
+ // drop incorrectly oriented glyphs
+ if ((symbol.writingMode == WritingModeType::Vertical) != placedSymbol.useVerticalMode) return;
}
if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
@@ -575,20 +542,17 @@ 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));
- sizeBinder.populateVertexVector(feature);
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0, placementZoom);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
// add the two triangles, referencing the four coordinates we just inserted.
buffer.triangles.emplace_back(index + 0, index + 1, index + 2);
@@ -596,6 +560,8 @@ void SymbolLayout::addSymbol(Buffer& buffer,
segment.vertexLength += vertexLength;
segment.indexLength += 6;
+
+ placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x);
}
void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) {
@@ -635,10 +601,10 @@ void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket&
auto& segment = collisionBox.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.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl, maxZoom, placementZoom));
+ collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr, maxZoom, placementZoom));
+ collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br, maxZoom, placementZoom));
+ collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl, maxZoom, placementZoom));
collisionBox.lines.emplace_back(index + 0, index + 1);
collisionBox.lines.emplace_back(index + 1, index + 2);
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 7d19aba671..90f5b3c91d 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -20,6 +20,7 @@ class CollisionTile;
class SymbolBucket;
class Anchor;
class RenderLayer;
+class PlacedSymbol;
namespace style {
class Filter;
@@ -29,32 +30,26 @@ class SymbolLayout {
public:
SymbolLayout(const BucketParameters&,
const std::vector<const RenderLayer*>&,
- const GeometryTileLayer&,
- IconDependencies&,
+ std::unique_ptr<GeometryTileLayer>,
+ ImageDependencies&,
GlyphDependencies&);
- void prepare(const GlyphPositionMap& glyphs, const IconMap& icons);
+ void prepare(const GlyphMap&, const GlyphPositions&,
+ const ImageMap&, const ImagePositions&);
std::unique_ptr<SymbolBucket> place(CollisionTile&);
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::Evaluated, style::TextPaintProperties::Evaluated>> layerPaintProperties;
+ std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties;
private:
void addFeature(const size_t,
const SymbolFeature&,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositions& face);
+ const GlyphPositionMap&);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
@@ -64,20 +59,22 @@ private:
// Adds placed items to the buffer.
template <typename Buffer>
void addSymbol(Buffer&,
- SymbolSizeBinder& sizeBinder,
+ 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);
- const std::string sourceLayerName;
+ // 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;
+ const float pixelRatio;
style::SymbolLayoutProperties::PossiblyEvaluated layout;
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
new file mode 100644
index 0000000000..279d251f8f
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -0,0 +1,358 @@
+#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/renderer/frame_history.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;
+ }
+
+
+ typedef std::pair<Point<float>,float> PointAndCameraDistance;
+
+ PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) {
+ vec4 pos = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(pos, pos, matrix);
+ 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 float placementZoom, const std::array<double, 2>& clippingBuffer, const FrameHistory& frameHistory) {
+ 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 && frameHistory.isVisible(placementZoom);
+ }
+
+ void addDynamicAttributes(const Point<float>& anchorPoint, const float angle, const float placementZoom,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle, placementZoom);
+ 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, 25, dynamicVertexArray);
+ }
+ }
+
+ struct PlacedGlyph {
+ PlacedGlyph(Point<float> point_, float angle_) : point(point_), angle(angle_) {}
+ Point<float> point;
+ float angle;
+ };
+
+ enum PlacementResult {
+ OK,
+ NotEnoughRoom,
+ NeedsFlipping
+ };
+
+ 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 mat4& labelPlaneMatrix) {
+
+ 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;
+
+ 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 }};
+ }
+
+ 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 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 float firstGlyphOffset = symbol.glyphOffsets.front();
+ const float lastGlyphOffset = symbol.glyphOffsets.back();
+
+ optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ if (!firstPlacedGlyph)
+ return PlacementResult::NotEnoughRoom;
+
+ optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ if (!lastPlacedGlyph)
+ return PlacementResult::NotEnoughRoom;
+
+ const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first;
+ const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first;
+
+ if (keepUpright && !flip &&
+ (symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) {
+ return PlacementResult::NeedsFlipping;
+ }
+
+ placedGlyphs.push_back(*firstPlacedGlyph);
+ for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) {
+ const float glyphOffsetX = symbol.glyphOffsets[glyphIndex];
+ // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed
+ auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ placedGlyphs.push_back(*placedGlyph);
+ }
+ placedGlyphs.push_back(*lastPlacedGlyph);
+ } 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);
+
+ if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) {
+ return PlacementResult::NeedsFlipping;
+ }
+ }
+ assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all
+ const float glyphOffsetX = symbol.glyphOffsets.front();
+ optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment,
+ symbol.line, labelPlaneMatrix);
+ if (!singleGlyph)
+ return PlacementResult::NotEnoughRoom;
+
+ placedGlyphs.push_back(*singleGlyph);
+ }
+
+ for (auto& placedGlyph : placedGlyphs) {
+ addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, 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 FrameHistory& frameHistory) {
+
+ 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();
+
+ for (auto& placedSymbol : placedSymbols) {
+ vec4 anchorPos = {{ placedSymbol.anchorPoint.x, placedSymbol.anchorPoint.y, 0, 1 }};
+ matrix::transformMat4(anchorPos, anchorPos, posMatrix);
+
+ // Don't bother calculating the correct point for invisible labels.
+ if (!isVisible(anchorPos, placedSymbol.placementZoom, clippingBuffer, frameHistory)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ continue;
+ }
+
+ const float cameraToAnchorDistance = anchorPos[3];
+ const float perspectiveRatio = 1 + 0.5 * ((cameraToAnchorDistance / state.getCameraToCenterDistance()) - 1.0);
+
+ 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);
+
+ if (placeUnflipped == PlacementResult::NotEnoughRoom ||
+ (placeUnflipped == PlacementResult::NeedsFlipping &&
+ placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint) == 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..2652fe7ace
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.hpp
@@ -0,0 +1,25 @@
+#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 FrameHistory;
+ class SymbolSizeBinder;
+ class PlacedSymbol;
+ namespace style {
+ class SymbolPropertyValues;
+ } // end namespace style
+
+ 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);
+
+ void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>&, const std::vector<PlacedSymbol>&,
+ const mat4& posMatrix, const style::SymbolPropertyValues&,
+ const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&, const FrameHistory& frameHistory);
+
+} // end namespace mbgl
diff --git a/src/mbgl/map/backend.cpp b/src/mbgl/map/backend.cpp
deleted file mode 100644
index 0b4fd01050..0000000000
--- a/src/mbgl/map/backend.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/context.hpp>
-#include <mbgl/gl/extension.hpp>
-#include <mbgl/gl/debugging.hpp>
-
-#include <cassert>
-
-namespace mbgl {
-
-Backend::Backend() = default;
-
-gl::Context& Backend::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));
- });
- return *context;
-}
-
-PremultipliedImage Backend::readFramebuffer(const Size& size) const {
- assert(context);
- return context->readFramebuffer<PremultipliedImage>(size);
-}
-
-void Backend::assumeFramebufferBinding(const gl::FramebufferID fbo) {
- getContext().bindFramebuffer.setCurrentValue(fbo);
- if (fbo != ImplicitFramebufferBinding) {
- assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
- }
-}
-void Backend::assumeViewportSize(const Size& size) {
- getContext().viewport.setCurrentValue({ 0, 0, size });
- assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue());
-}
-
-bool Backend::implicitFramebufferBound() {
- return getContext().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding;
-}
-
-void Backend::setFramebufferBinding(const gl::FramebufferID fbo) {
- getContext().bindFramebuffer = fbo;
- if (fbo != ImplicitFramebufferBinding) {
- assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
- }
-}
-
-void Backend::setViewportSize(const Size& size) {
- getContext().viewport = { 0, 0, size };
- assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue());
-}
-
-Backend::~Backend() = default;
-
-} // namespace mbgl
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 35457f3a5b..7534fe67ad 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,180 +1,149 @@
#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.hpp>
-#include <mbgl/style/source.hpp>
-#include <mbgl/style/layer.hpp>
-#include <mbgl/style/light.hpp>
+#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/observer.hpp>
-#include <mbgl/style/transition_options.hpp>
#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/render_source.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/exception.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>
#include <mbgl/util/logging.hpp>
#include <mbgl/math/log2.hpp>
+#include <utility>
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 {
+class Map::Impl : public style::Observer,
+ 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 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();
- void loadStyleJSON(const std::string&);
+ // 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;
-
- std::unique_ptr<AnnotationManager> annotationManager;
- std::unique_ptr<Painter> painter;
std::unique_ptr<Style> style;
+ AnnotationManager annotationManager;
- std::string styleURL;
- std::string styleJSON;
- bool styleMutated = false;
bool cameraMutated = false;
- std::unique_ptr<AsyncRequest> styleRequest;
+ uint8_t prefetchZoomDelta = util::DEFAULT_PREFETCH_ZOOM_DELTA;
- size_t sourceCacheSize;
bool loading = false;
-
- util::AsyncTask asyncInvalidate;
+ 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(programCacheDir_),
- annotationManager(std::make_unique<AnnotationManager>(pixelRatio)),
- asyncInvalidate([this] {
- if (mode == MapMode::Continuous) {
- backend.invalidate();
- } else {
- renderStill();
- }
- }) {
-}
+ style(std::make_unique<Style>(scheduler, fileSource, pixelRatio)),
+ annotationManager(*style) {
-Map::~Map() {
- BackendScope guard(impl->backend);
+ style->impl->setObserver(this);
+ rendererFrontend.setObserver(*this);
+}
- impl->styleRequest = nullptr;
+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->style.reset();
- impl->annotationManager.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;
@@ -190,238 +159,93 @@ void Map::renderStill(View& view, StillImageCallback callback) {
return;
}
- if (!impl->style) {
- callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")));
+ if (impl->style->impl->getLastError()) {
+ callback(impl->style->impl->getLastError());
return;
}
- if (impl->style->getLastError()) {
- callback(impl->style->getLastError());
- return;
- }
+ impl->stillImageRequest = std::make_unique<StillImageRequest>(std::move(callback));
- impl->stillImageRequest = std::make_unique<StillImageRequest>(view, std::move(callback));
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
-void Map::Impl::renderStill() {
- if (!stillImageRequest) {
- return;
- }
-
- // TODO: determine whether we need activate/deactivate
- BackendScope guard(backend);
- render(stillImageRequest->view);
+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::triggerRepaint() {
- impl->backend.invalidate();
-}
-
-void Map::render(View& view) {
- impl->render(view);
+ impl->onUpdate();
}
-void Map::Impl::render(View& view) {
- if (!style) {
- return;
- }
-
- TimePoint timePoint = mode == MapMode::Continuous
- ? Clock::now()
- : Clock::time_point::max();
-
- transform.updateTransitions(timePoint);
-
- if (style->loaded && updateFlags & Update::AnnotationStyle) {
- annotationManager->updateStyle(*style);
- }
-
- if (updateFlags & Update::AnnotationData) {
- annotationManager->updateData();
- }
-
- style->update({
- mode,
- updateFlags,
- pixelRatio,
- debugOptions,
- timePoint,
- transform.getState(),
- scheduler,
- fileSource,
- *annotationManager
- });
+#pragma mark - Map::Impl RendererObserver
- updateFlags = Update::Nothing;
-
- gl::Context& context = backend.getContext();
- if (!painter) {
- painter = std::make_unique<Painter>(context, transform.getState(), pixelRatio, programCacheDir);
+void Map::Impl::onWillStartRenderingMap() {
+ if (mode == MapMode::Continuous) {
+ observer.onWillStartRenderingMap();
}
+}
+void Map::Impl::onWillStartRenderingFrame() {
if (mode == MapMode::Continuous) {
- if (renderState == RenderState::Never) {
- observer.onWillStartRenderingMap();
- }
-
observer.onWillStartRenderingFrame();
+ }
+}
- FrameData frameData { timePoint,
- pixelRatio,
- mode,
- contextMode,
- debugOptions };
-
- backend.updateAssumedState();
-
- painter->render(*style,
- frameData,
- view,
- annotationManager->getSpriteAtlas());
-
- painter->cleanup();
-
- observer.onDidFinishRenderingFrame(style->isLoaded() ? MapObserver::RenderMode::Full : MapObserver::RenderMode::Partial);
+void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepaint) {
+ rendererFullyLoaded = renderMode == RenderMode::Full;
- if (!style->isLoaded()) {
- renderState = RenderState::Partial;
- } else if (renderState != RenderState::Fully) {
- renderState = RenderState::Fully;
- observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full);
- if (loading) {
- loading = false;
- observer.onDidFinishLoadingMap();
- }
- }
+ if (mode == MapMode::Continuous) {
+ 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 (style->hasTransitions() || painter->needsAnimation() || transform.inTransition()) {
- onUpdate(Update::Repaint);
+ if (needsRepaint || transform.inTransition()) {
+ onUpdate();
}
- } else if (stillImageRequest && style->isLoaded()) {
- FrameData frameData { timePoint,
- pixelRatio,
- mode,
- contextMode,
- debugOptions };
-
- backend.updateAssumedState();
-
- painter->render(*style,
- frameData,
- view,
- annotationManager->getSpriteAtlas());
-
+ } else if (stillImageRequest && rendererFullyLoaded) {
auto request = std::move(stillImageRequest);
request->callback(nullptr);
-
- painter->cleanup();
}
}
-#pragma mark - Style
-
-void Map::setStyleURL(const std::string& url) {
- if (impl->styleURL == url) {
- return;
- }
-
- impl->loading = true;
-
- impl->observer.onWillStartLoadingMap();
-
- impl->styleRequest = nullptr;
- impl->styleURL = url;
- impl->styleJSON.clear();
- impl->styleMutated = false;
-
- impl->style = std::make_unique<Style>(impl->scheduler, impl->fileSource, impl->pixelRatio);
-
- impl->styleRequest = impl->fileSource.request(Resource::style(impl->styleURL), [this](Response res) {
- // Once we get a fresh style, or the style is mutated, stop revalidating.
- if (res.isFresh() || impl->styleMutated) {
- impl->styleRequest.reset();
- }
-
- // Don't allow a loaded, mutated style to be overwritten with a new version.
- if (impl->styleMutated && impl->style->loaded) {
- return;
+void Map::Impl::onDidFinishRenderingMap() {
+ if (mode == MapMode::Continuous && loading) {
+ observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full);
+ if (loading) {
+ loading = false;
+ observer.onDidFinishLoadingMap();
}
-
- if (res.error) {
- if (res.error->reason == Response::Error::Reason::NotFound &&
- util::mapbox::isMapboxURL(impl->styleURL)) {
- const std::string message = "style " + impl->styleURL + " could not be found or is an incompatible legacy map or style";
- Log::Error(Event::Setup, message.c_str());
- impl->onStyleError(std::make_exception_ptr(util::NotFoundException(message)));
- } else {
- const std::string message = "loading style failed: " + res.error->message;
- Log::Error(Event::Setup, message.c_str());
- impl->onStyleError(std::make_exception_ptr(util::StyleLoadException(message)));
- }
- impl->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified || res.noContent) {
- return;
- } else {
- impl->loadStyleJSON(*res.data);
- }
- });
-}
-
-void Map::setStyleJSON(const std::string& json) {
- if (impl->styleJSON == json) {
- return;
}
+};
- impl->loading = true;
-
- impl->observer.onWillStartLoadingMap();
-
- impl->styleURL.clear();
- impl->styleJSON.clear();
- impl->styleMutated = false;
-
- impl->style = std::make_unique<Style>(impl->scheduler, impl->fileSource, impl->pixelRatio);
-
- impl->loadStyleJSON(json);
-}
-
-void Map::Impl::loadStyleJSON(const std::string& json) {
- style->setObserver(this);
- style->setJSON(json);
- styleJSON = json;
-
- if (!cameraMutated) {
- // Zoom first because it may constrain subsequent operations.
- map.setZoom(map.getDefaultZoom());
- map.setLatLng(map.getDefaultLatLng());
- map.setBearing(map.getDefaultBearing());
- map.setPitch(map.getDefaultPitch());
- }
+#pragma mark - Style
- onUpdate(Update::Classes | Update::AnnotationStyle);
+style::Style& Map::getStyle() {
+ return *impl->style;
}
-std::string Map::getStyleURL() const {
- return impl->styleURL;
+const style::Style& Map::getStyle() const {
+ return *impl->style;
}
-std::string Map::getStyleJSON() const {
- return impl->styleJSON;
+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 {
@@ -449,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
@@ -469,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) {
@@ -480,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 {
@@ -502,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();
}
@@ -516,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 {
@@ -537,7 +361,7 @@ 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 {
@@ -627,7 +451,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) {
@@ -678,7 +502,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 {
@@ -690,7 +514,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) {
@@ -701,13 +525,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 {
@@ -717,7 +541,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
@@ -730,7 +554,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 {
@@ -741,7 +565,7 @@ double Map::getPitch() const {
void Map::setNorthOrientation(NorthOrientation orientation) {
impl->transform.setNorthOrientation(orientation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
NorthOrientation Map::getNorthOrientation() const {
@@ -752,7 +576,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 {
@@ -763,7 +587,7 @@ ConstrainMode Map::getConstrainMode() const {
void Map::setViewportMode(mbgl::ViewportMode mode) {
impl->transform.setViewportMode(mode);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
ViewportMode Map::getViewportMode() const {
@@ -787,239 +611,40 @@ LatLng Map::latLngForPixel(const ScreenCoordinate& pixel) const {
#pragma mark - Annotations
-void Map::addAnnotationImage(const std::string& id, std::unique_ptr<style::Image> image) {
- impl->annotationManager->addImage(id, std::move(image));
+void Map::addAnnotationImage(std::unique_ptr<style::Image> image) {
+ impl->annotationManager.addImage(std::move(image));
}
void Map::removeAnnotationImage(const std::string& id) {
- impl->annotationManager->removeImage(id);
+ impl->annotationManager.removeImage(id);
}
double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
- return impl->annotationManager->getTopOffsetPixelsForImage(id);
+ return impl->annotationManager.getTopOffsetPixelsForImage(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, getMaxZoom());
+ impl->onUpdate();
return result;
}
void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) {
- impl->onUpdate(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom()));
-}
-
-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->style) return {};
-
- return impl->style->queryRenderedFeatures(
- { point },
- impl->transform.getState(),
- options
- );
-}
-
-std::vector<Feature> Map::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) {
- if (!impl->style) return {};
-
- return impl->style->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->style) return {};
-
- const RenderSource* source = impl->style->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>()));
+ if (impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom())) {
+ impl->onUpdate();
}
- AnnotationIDs ids;
- ids.reserve(set.size());
- std::move(set.begin(), set.end(), std::back_inserter(ids));
- return ids;
}
-#pragma mark - Style API
-
-std::vector<style::Source*> Map::getSources() {
- return impl->style ? impl->style->getSources() : std::vector<style::Source*>();
-}
-
-style::Source* Map::getSource(const std::string& sourceID) {
- if (impl->style) {
- impl->styleMutated = true;
- return impl->style->getSource(sourceID);
- }
- return nullptr;
-}
-
-void Map::addSource(std::unique_ptr<style::Source> source) {
- if (impl->style) {
- impl->styleMutated = true;
- impl->style->addSource(std::move(source));
- }
-}
-
-std::unique_ptr<Source> Map::removeSource(const std::string& sourceID) {
- if (impl->style) {
- impl->styleMutated = true;
- return impl->style->removeSource(sourceID);
- }
- return nullptr;
-}
-
-std::vector<style::Layer*> Map::getLayers() {
- return impl->style ? impl->style->getLayers() : std::vector<style::Layer*>();
-}
-
-Layer* Map::getLayer(const std::string& layerID) {
- if (impl->style) {
- impl->styleMutated = true;
- return impl->style->getLayer(layerID);
- }
- return nullptr;
-}
-
-void Map::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& before) {
- if (!impl->style) {
- return;
- }
-
- impl->styleMutated = true;
- BackendScope guard(impl->backend);
-
- impl->style->addLayer(std::move(layer), before);
- impl->onUpdate(Update::Classes);
-}
-
-std::unique_ptr<Layer> Map::removeLayer(const std::string& id) {
- if (!impl->style) {
- return nullptr;
- }
-
- impl->styleMutated = true;
- BackendScope guard(impl->backend);
-
- auto removedLayer = impl->style->removeLayer(id);
- impl->onUpdate(Update::Repaint);
-
- return removedLayer;
-}
-
-void Map::addImage(const std::string& id, std::unique_ptr<style::Image> image) {
- if (!impl->style) {
- return;
- }
-
- impl->styleMutated = true;
- impl->style->spriteAtlas->addImage(id, std::move(image));
- impl->onUpdate(Update::Repaint);
-}
-
-void Map::removeImage(const std::string& id) {
- if (!impl->style) {
- return;
- }
-
- impl->styleMutated = true;
- impl->style->spriteAtlas->removeImage(id);
- impl->onUpdate(Update::Repaint);
-}
-
-const style::Image* Map::getImage(const std::string& id) {
- if (impl->style) {
- return impl->style->spriteAtlas->getImage(id);
- }
- return nullptr;
-}
-
-void Map::setLight(std::unique_ptr<style::Light> light) {
- if (!impl->style) {
- return;
- }
-
- impl->style->setLight(std::move(light));
-}
-
-style::Light* Map::getLight() {
- if (!impl->style) {
- return nullptr;
- }
-
- return impl->style->getLight();
-}
-
-#pragma mark - Defaults
-
-std::string Map::getStyleName() const {
- if (impl->style) {
- return impl->style->getName();
- }
- return {};
-}
-
-LatLng Map::getDefaultLatLng() const {
- if (impl->style) {
- return impl->style->getDefaultLatLng();
- }
- return {};
-}
-
-double Map::getDefaultZoom() const {
- if (impl->style) {
- return impl->style->getDefaultZoom();
- }
- return {};
-}
-
-double Map::getDefaultBearing() const {
- if (impl->style) {
- return impl->style->getDefaultBearing();
- }
- return {};
-}
-
-double Map::getDefaultPitch() const {
- if (impl->style) {
- return impl->style->getDefaultPitch();
- }
- return {};
+void Map::removeAnnotation(AnnotationID annotation) {
+ impl->annotationManager.removeAnnotation(annotation);
+ impl->onUpdate();
}
#pragma mark - Toggles
void Map::setDebug(MapDebugOptions debugOptions) {
impl->debugOptions = debugOptions;
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::cycleDebugOptions() {
@@ -1043,90 +668,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->style->isLoaded() : false;
-}
-
-void Map::addClass(const std::string& className) {
- if (impl->style && impl->style->addClass(className)) {
- impl->onUpdate(Update::Classes);
- }
-}
-
-void Map::removeClass(const std::string& className) {
- if (impl->style && impl->style->removeClass(className)) {
- impl->onUpdate(Update::Classes);
- }
+void Map::setPrefetchZoomDelta(uint8_t delta) {
+ impl->prefetchZoomDelta = delta;
}
-void Map::setClasses(const std::vector<std::string>& classNames) {
- if (impl->style) {
- impl->style->setClasses(classNames);
- impl->onUpdate(Update::Classes);
- }
+uint8_t Map::getPrefetchZoomDelta() const {
+ return impl->prefetchZoomDelta;
}
-style::TransitionOptions Map::getTransitionOptions() const {
- if (impl->style) {
- return impl->style->getTransitionOptions();
- }
- return {};
+bool Map::isFullyLoaded() const {
+ return impl->style->impl->isLoaded() && impl->rendererFullyLoaded;
}
-void Map::setTransitionOptions(const style::TransitionOptions& options) {
- if (impl->style) {
- impl->style->setTransitionOptions(options);
- }
+void Map::Impl::onSourceChanged(style::Source& source) {
+ observer.onSourceChanged(source);
}
-bool Map::hasClass(const std::string& className) const {
- return impl->style ? impl->style->hasClass(className) : false;
+void Map::Impl::onInvalidate() {
+ onUpdate();
}
-std::vector<std::string> Map::getClasses() const {
- if (impl->style) {
- return impl->style->getClasses();
- }
- return {};
-}
+void Map::Impl::onUpdate() {
+ TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max();
-void Map::setSourceTileCacheSize(size_t size) {
- if (size != impl->sourceCacheSize) {
- impl->sourceCacheSize = size;
- if (!impl->style) return;
- impl->style->setSourceTileCacheSize(size);
- impl->backend.invalidate();
- }
-}
+ transform.updateTransitions(timePoint);
-void Map::onLowMemory() {
- if (impl->painter) {
- BackendScope guard(impl->backend);
- impl->painter->cleanup();
- }
- if (impl->style) {
- impl->style->onLowMemory();
- impl->backend.invalidate();
- }
-}
+ 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)
+ };
-void Map::Impl::onSourceChanged(style::Source& source) {
- observer.onSourceChanged(source);
+ rendererFrontend.update(std::make_shared<UpdateParameters>(std::move(params)));
}
-void Map::Impl::onUpdate(Update flags) {
- updateFlags |= flags;
- asyncInvalidate.send();
+void Map::Impl::onStyleLoading() {
+ loading = true;
+ rendererFullyLoaded = false;
+ observer.onWillStartLoadingMap();
}
void Map::Impl::onStyleLoaded() {
+ if (!cameraMutated) {
+ map.jumpTo(style->getDefaultCamera());
+ }
+
+ annotationManager.onStyleLoaded();
observer.onDidFinishLoadingStyle();
}
@@ -1143,12 +750,7 @@ void Map::Impl::onResourceError(std::exception_ptr error) {
void Map::dumpDebugLogs() const {
Log::Info(Event::General, "--------------------------------------------------------------------------------");
- Log::Info(Event::General, "MapContext::styleURL: %s", impl->styleURL.c_str());
- if (impl->style) {
- impl->style->dumpDebugLogs();
- } else {
- Log::Info(Event::General, "no style loaded");
- }
+ impl->style->impl->dumpDebugLogs();
Log::Info(Event::General, "--------------------------------------------------------------------------------");
}
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 8d05bc0e91..2bb25af28f 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);
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index bbf7e22b31..d1a320beae 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -132,7 +132,7 @@ double TransformState::getZoom() const {
return scaleZoom(scale);
}
-int32_t TransformState::getIntegerZoom() const {
+uint8_t TransformState::getIntegerZoom() const {
return getZoom();
}
@@ -358,7 +358,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 +385,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..59522d89fd 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;
diff --git a/src/mbgl/map/update.hpp b/src/mbgl/map/update.hpp
deleted file mode 100644
index 5e87515eac..0000000000
--- a/src/mbgl/map/update.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <mbgl/util/traits.hpp>
-
-namespace mbgl {
-
-enum class Update {
- Nothing = 0,
- Repaint = 1 << 0,
- Classes = 1 << 2,
- RecalculateStyle = 1 << 3,
- 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 5e3166d082..d023ec7d83 100644
--- a/src/mbgl/programs/attributes.hpp
+++ b/src/mbgl/programs/attributes.hpp
@@ -24,6 +24,9 @@ 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);
@@ -97,6 +100,11 @@ struct a_width {
using Type = gl::Attribute<float, 1>;
};
+struct a_floorwidth {
+ static auto name() { return "a_floorwidth"; }
+ using Type = gl::Attribute<float, 1>;
+};
+
struct a_height {
static auto name() { return "a_height"; }
using Type = gl::Attribute<float, 1>;
diff --git a/src/mbgl/programs/binary_program.cpp b/src/mbgl/programs/binary_program.cpp
index 1cad0a2693..da629194b4 100644
--- a/src/mbgl/programs/binary_program.cpp
+++ b/src/mbgl/programs/binary_program.cpp
@@ -2,6 +2,8 @@
#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) {
@@ -64,12 +66,12 @@ BinaryProgram::BinaryProgram(std::string&& data) {
BinaryProgram::BinaryProgram(
gl::BinaryProgramFormat binaryFormat_,
std::string&& binaryCode_,
- const std::string& binaryIdentifier_,
+ std::string binaryIdentifier_,
std::vector<std::pair<const std::string, gl::AttributeLocation>>&& attributes_,
std::vector<std::pair<const std::string, gl::UniformLocation>>&& uniforms_)
: binaryFormat(binaryFormat_),
binaryCode(std::move(binaryCode_)),
- binaryIdentifier(binaryIdentifier_),
+ binaryIdentifier(std::move(binaryIdentifier_)),
attributes(std::move(attributes_)),
uniforms(std::move(uniforms_)) {
}
diff --git a/src/mbgl/programs/binary_program.hpp b/src/mbgl/programs/binary_program.hpp
index 2806f4fb3e..8690f3fd6f 100644
--- a/src/mbgl/programs/binary_program.hpp
+++ b/src/mbgl/programs/binary_program.hpp
@@ -15,7 +15,7 @@ public:
BinaryProgram(gl::BinaryProgramFormat,
std::string&& binaryCode,
- const std::string& binaryIdentifier,
+ std::string binaryIdentifier,
std::vector<std::pair<const std::string, gl::AttributeLocation>>&&,
std::vector<std::pair<const std::string, gl::UniformLocation>>&&);
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 89b69484fd..ba99e0c087 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/style/properties.hpp>
#include <mbgl/util/geometry.hpp>
#include <cmath>
@@ -17,6 +18,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom);
using CollisionBoxAttributes = gl::Attributes<
attributes::a_pos,
+ attributes::a_anchor_pos,
attributes::a_extrude,
attributes::a_data<uint8_t, 2>>;
@@ -28,19 +30,27 @@ class CollisionBoxProgram : public Program<
uniforms::u_matrix,
uniforms::u_scale,
uniforms::u_zoom,
- uniforms::u_maxzoom>,
- style::PaintProperties<>>
+ uniforms::u_maxzoom,
+ uniforms::u_collision_y_stretch,
+ uniforms::u_camera_to_center_distance,
+ uniforms::u_pitch,
+ uniforms::u_fadetexture>,
+ style::Properties<>>
{
public:
using Program::Program;
- static LayoutVertex vertex(Point<float> a, Point<float> o, float maxzoom, float placementZoom) {
+ static LayoutVertex vertex(Point<float> a, Point<float> anchor, Point<float> o, float maxzoom, float placementZoom) {
return LayoutVertex {
{{
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))
}},
diff --git a/src/mbgl/programs/debug_program.hpp b/src/mbgl/programs/debug_program.hpp
index de1666b4a8..7a6d075cdb 100644
--- a/src/mbgl/programs/debug_program.hpp
+++ b/src/mbgl/programs/debug_program.hpp
@@ -4,6 +4,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/debug.hpp>
+#include <mbgl/style/properties.hpp>
namespace mbgl {
@@ -15,7 +16,7 @@ class DebugProgram : public Program<
gl::Uniforms<
uniforms::u_matrix,
uniforms::u_color>,
- style::PaintProperties<>>
+ style::Properties<>>
{
public:
using Program::Program;
diff --git a/src/mbgl/programs/extrusion_texture_program.hpp b/src/mbgl/programs/extrusion_texture_program.hpp
index 1519aa095d..bd82208885 100644
--- a/src/mbgl/programs/extrusion_texture_program.hpp
+++ b/src/mbgl/programs/extrusion_texture_program.hpp
@@ -4,6 +4,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/extrusion_texture.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/util/geometry.hpp>
namespace mbgl {
@@ -17,7 +18,7 @@ class ExtrusionTextureProgram : public Program<
uniforms::u_world,
uniforms::u_image,
uniforms::u_opacity>,
- style::PaintProperties<>> {
+ style::Properties<>> {
public:
using Program::Program;
diff --git a/src/mbgl/programs/fill_extrusion_program.cpp b/src/mbgl/programs/fill_extrusion_program.cpp
index 63d1cbeb59..aaf192a843 100644
--- a/src/mbgl/programs/fill_extrusion_program.cpp
+++ b/src/mbgl/programs/fill_extrusion_program.cpp
@@ -1,5 +1,5 @@
#include <mbgl/programs/fill_extrusion_program.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/map/transform_state.hpp>
@@ -45,8 +45,9 @@ FillExtrusionUniforms::values(mat4 matrix,
FillExtrusionPatternUniforms::Values
FillExtrusionPatternUniforms::values(mat4 matrix,
- const SpriteAtlasElement& a,
- const SpriteAtlasElement& b,
+ Size atlasSize,
+ const ImagePosition& a,
+ const ImagePosition& b,
const Faded<std::string>& fading,
const UnwrappedTileID& tileID,
const TransformState& state,
@@ -58,14 +59,15 @@ FillExtrusionPatternUniforms::values(mat4 matrix,
return FillExtrusionPatternUniforms::Values{
uniforms::u_matrix::Value{ matrix },
- uniforms::u_pattern_tl_a::Value{ a.tl },
- uniforms::u_pattern_br_a::Value{ a.br },
- uniforms::u_pattern_tl_b::Value{ b.tl },
- uniforms::u_pattern_br_b::Value{ b.br },
- uniforms::u_pattern_size_a::Value{ a.size },
- uniforms::u_pattern_size_b::Value{ b.size },
+ uniforms::u_pattern_tl_a::Value{ a.tl() },
+ uniforms::u_pattern_br_a::Value{ a.br() },
+ uniforms::u_pattern_tl_b::Value{ b.tl() },
+ uniforms::u_pattern_br_b::Value{ b.br() },
+ uniforms::u_pattern_size_a::Value{ a.displaySize() },
+ uniforms::u_pattern_size_b::Value{ b.displaySize() },
uniforms::u_scale_a::Value{ fading.fromScale },
uniforms::u_scale_b::Value{ fading.toScale },
+ uniforms::u_texsize::Value{ atlasSize },
uniforms::u_mix::Value{ fading.t },
uniforms::u_image::Value{ 0 },
uniforms::u_pixel_coord_upper::Value{ std::array<float, 2>{{ float(pixelX >> 16), float(pixelY >> 16) }} },
diff --git a/src/mbgl/programs/fill_extrusion_program.hpp b/src/mbgl/programs/fill_extrusion_program.hpp
index 48fca44ee8..820670068e 100644
--- a/src/mbgl/programs/fill_extrusion_program.hpp
+++ b/src/mbgl/programs/fill_extrusion_program.hpp
@@ -16,7 +16,7 @@
namespace mbgl {
-class SpriteAtlasElement;
+class ImagePosition;
class UnwrappedTileID;
class TransformState;
template <class> class Faded;
@@ -55,6 +55,7 @@ struct FillExtrusionPatternUniforms : gl::Uniforms<
uniforms::u_pattern_size_b,
uniforms::u_scale_a,
uniforms::u_scale_b,
+ uniforms::u_texsize,
uniforms::u_mix,
uniforms::u_image,
uniforms::u_pixel_coord_upper,
@@ -66,8 +67,9 @@ struct FillExtrusionPatternUniforms : gl::Uniforms<
uniforms::u_lightintensity>
{
static Values values(mat4,
- const SpriteAtlasElement&,
- const SpriteAtlasElement&,
+ Size atlasSize,
+ const ImagePosition&,
+ const ImagePosition&,
const Faded<std::string>&,
const UnwrappedTileID&,
const TransformState&,
diff --git a/src/mbgl/programs/fill_program.cpp b/src/mbgl/programs/fill_program.cpp
index 4310f01164..46dc830102 100644
--- a/src/mbgl/programs/fill_program.cpp
+++ b/src/mbgl/programs/fill_program.cpp
@@ -1,5 +1,5 @@
#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/map/transform_state.hpp>
@@ -13,8 +13,9 @@ static_assert(sizeof(FillLayoutVertex) == 4, "expected FillLayoutVertex size");
FillPatternUniforms::Values
FillPatternUniforms::values(mat4 matrix,
Size framebufferSize,
- const SpriteAtlasElement& a,
- const SpriteAtlasElement& b,
+ Size atlasSize,
+ const ImagePosition& a,
+ const ImagePosition& b,
const Faded<std::string>& fading,
const UnwrappedTileID& tileID,
const TransformState& state)
@@ -26,12 +27,13 @@ FillPatternUniforms::values(mat4 matrix,
return FillPatternUniforms::Values {
uniforms::u_matrix::Value{ matrix },
uniforms::u_world::Value{ framebufferSize },
- uniforms::u_pattern_tl_a::Value{ a.tl },
- uniforms::u_pattern_br_a::Value{ a.br },
- uniforms::u_pattern_tl_b::Value{ b.tl },
- uniforms::u_pattern_br_b::Value{ b.br },
- uniforms::u_pattern_size_a::Value{ a.size },
- uniforms::u_pattern_size_b::Value{ b.size },
+ uniforms::u_texsize::Value{ atlasSize },
+ uniforms::u_pattern_tl_a::Value{ a.tl() },
+ uniforms::u_pattern_br_a::Value{ a.br() },
+ uniforms::u_pattern_tl_b::Value{ b.tl() },
+ uniforms::u_pattern_br_b::Value{ b.br() },
+ uniforms::u_pattern_size_a::Value{ a.displaySize() },
+ uniforms::u_pattern_size_b::Value{ b.displaySize() },
uniforms::u_scale_a::Value{ fading.fromScale },
uniforms::u_scale_b::Value{ fading.toScale },
uniforms::u_mix::Value{ fading.t },
diff --git a/src/mbgl/programs/fill_program.hpp b/src/mbgl/programs/fill_program.hpp
index 63751e740a..2dfeea3279 100644
--- a/src/mbgl/programs/fill_program.hpp
+++ b/src/mbgl/programs/fill_program.hpp
@@ -16,7 +16,7 @@
namespace mbgl {
-class SpriteAtlasElement;
+class ImagePosition;
class UnwrappedTileID;
class TransformState;
template <class> class Faded;
@@ -33,6 +33,7 @@ struct FillUniforms : gl::Uniforms<
struct FillPatternUniforms : gl::Uniforms<
uniforms::u_matrix,
uniforms::u_world,
+ uniforms::u_texsize,
uniforms::u_pattern_tl_a,
uniforms::u_pattern_br_a,
uniforms::u_pattern_tl_b,
@@ -49,8 +50,9 @@ struct FillPatternUniforms : gl::Uniforms<
{
static Values values(mat4 matrix,
Size framebufferSize,
- const SpriteAtlasElement&,
- const SpriteAtlasElement&,
+ Size atlasSize,
+ const ImagePosition&,
+ const ImagePosition&,
const Faded<std::string>&,
const UnwrappedTileID&,
const TransformState&);
diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp
index 58e0410b17..faf57ef19b 100644
--- a/src/mbgl/programs/line_program.cpp
+++ b/src/mbgl/programs/line_program.cpp
@@ -1,9 +1,9 @@
#include <mbgl/programs/line_program.hpp>
#include <mbgl/style/layers/line_layer_properties.hpp>
#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/util/mat2.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
namespace mbgl {
@@ -13,7 +13,7 @@ using namespace style;
static_assert(sizeof(LineLayoutVertex) == 12, "expected LineLayoutVertex size");
template <class Values, class...Args>
-Values makeValues(const LinePaintProperties::Evaluated& properties,
+Values makeValues(const RenderLinePaintProperties::PossiblyEvaluated& properties,
const RenderTile& tile,
const TransformState& state,
const std::array<float, 2>& pixelsToGLUnits,
@@ -25,7 +25,6 @@ Values makeValues(const LinePaintProperties::Evaluated& properties,
properties.get<LineTranslateAnchor>(),
state)
},
- uniforms::u_width::Value{ properties.get<LineWidth>() },
uniforms::u_ratio::Value{ 1.0f / tile.id.pixelsToTileUnits(1.0, state.getZoom()) },
uniforms::u_gl_units_to_pixels::Value{{{ 1.0f / pixelsToGLUnits[0], 1.0f / pixelsToGLUnits[1] }}},
std::forward<Args>(args)...
@@ -33,7 +32,7 @@ Values makeValues(const LinePaintProperties::Evaluated& properties,
}
LineProgram::UniformValues
-LineProgram::uniformValues(const LinePaintProperties::Evaluated& properties,
+LineProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties,
const RenderTile& tile,
const TransformState& state,
const std::array<float, 2>& pixelsToGLUnits) {
@@ -46,17 +45,16 @@ LineProgram::uniformValues(const LinePaintProperties::Evaluated& properties,
}
LineSDFProgram::UniformValues
-LineSDFProgram::uniformValues(const LinePaintProperties::Evaluated& properties,
+LineSDFProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties,
float pixelRatio,
const RenderTile& tile,
const TransformState& state,
const std::array<float, 2>& pixelsToGLUnits,
const LinePatternPos& posA,
const LinePatternPos& posB,
- float dashLineWidth,
float atlasWidth) {
- const float widthA = posA.width * properties.get<LineDasharray>().fromScale * dashLineWidth;
- const float widthB = posB.width * properties.get<LineDasharray>().toScale * dashLineWidth;
+ const float widthA = posA.width * properties.get<LineDasharray>().fromScale;
+ const float widthB = posB.width * properties.get<LineDasharray>().toScale;
std::array<float, 2> scaleA {{
1.0f / tile.id.pixelsToTileUnits(widthA, state.getIntegerZoom()),
@@ -84,20 +82,21 @@ LineSDFProgram::uniformValues(const LinePaintProperties::Evaluated& properties,
}
LinePatternProgram::UniformValues
-LinePatternProgram::uniformValues(const LinePaintProperties::Evaluated& properties,
+LinePatternProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties,
const RenderTile& tile,
const TransformState& state,
const std::array<float, 2>& pixelsToGLUnits,
- const SpriteAtlasElement& posA,
- const SpriteAtlasElement& posB) {
+ const Size atlasSize,
+ const ImagePosition& posA,
+ const ImagePosition& posB) {
std::array<float, 2> sizeA {{
- tile.id.pixelsToTileUnits(posA.size[0] * properties.get<LinePattern>().fromScale, state.getIntegerZoom()),
- posA.size[1]
+ tile.id.pixelsToTileUnits(posA.displaySize()[0] * properties.get<LinePattern>().fromScale, state.getIntegerZoom()),
+ posA.displaySize()[1]
}};
std::array<float, 2> sizeB {{
- tile.id.pixelsToTileUnits(posB.size[0] * properties.get<LinePattern>().toScale, state.getIntegerZoom()),
- posB.size[1]
+ tile.id.pixelsToTileUnits(posB.displaySize()[0] * properties.get<LinePattern>().toScale, state.getIntegerZoom()),
+ posB.displaySize()[1]
}};
return makeValues<LinePatternProgram::UniformValues>(
@@ -105,12 +104,13 @@ LinePatternProgram::uniformValues(const LinePaintProperties::Evaluated& properti
tile,
state,
pixelsToGLUnits,
- uniforms::u_pattern_tl_a::Value{ posA.tl },
- uniforms::u_pattern_br_a::Value{ posA.br },
- uniforms::u_pattern_tl_b::Value{ posB.tl },
- uniforms::u_pattern_br_b::Value{ posB.br },
+ uniforms::u_pattern_tl_a::Value{ posA.tl() },
+ uniforms::u_pattern_br_a::Value{ posA.br() },
+ uniforms::u_pattern_tl_b::Value{ posB.tl() },
+ uniforms::u_pattern_br_b::Value{ posB.br() },
uniforms::u_pattern_size_a::Value{ sizeA },
uniforms::u_pattern_size_b::Value{ sizeB },
+ uniforms::u_texsize::Value{ atlasSize },
uniforms::u_fade::Value{ properties.get<LinePattern>().t },
uniforms::u_image::Value{ 0 }
);
diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp
index 9cfc2d3257..da9964e623 100644
--- a/src/mbgl/programs/line_program.hpp
+++ b/src/mbgl/programs/line_program.hpp
@@ -7,7 +7,7 @@
#include <mbgl/shaders/line_pattern.hpp>
#include <mbgl/shaders/line_sdf.hpp>
#include <mbgl/util/geometry.hpp>
-#include <mbgl/style/layers/line_layer_properties.hpp>
+#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <cmath>
@@ -16,11 +16,10 @@ namespace mbgl {
class RenderTile;
class TransformState;
class LinePatternPos;
-class SpriteAtlasElement;
+class ImagePosition;
namespace uniforms {
MBGL_DEFINE_UNIFORM_SCALAR(float, u_ratio);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_width);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_tex_y_a);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_tex_y_b);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_sdfgamma);
@@ -41,10 +40,9 @@ class LineProgram : public Program<
LineLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
- uniforms::u_width,
uniforms::u_ratio,
uniforms::u_gl_units_to_pixels>,
- style::LinePaintProperties>
+ RenderLinePaintProperties>
{
public:
using Program::Program;
@@ -94,7 +92,7 @@ public:
*/
static const int8_t extrudeScale = 63;
- static UniformValues uniformValues(const style::LinePaintProperties::Evaluated&,
+ static UniformValues uniformValues(const RenderLinePaintProperties::PossiblyEvaluated&,
const RenderTile&,
const TransformState&,
const std::array<float, 2>& pixelsToGLUnits);
@@ -106,7 +104,6 @@ class LinePatternProgram : public Program<
LineLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
- uniforms::u_width,
uniforms::u_ratio,
uniforms::u_gl_units_to_pixels,
uniforms::u_pattern_tl_a,
@@ -115,19 +112,21 @@ class LinePatternProgram : public Program<
uniforms::u_pattern_br_b,
uniforms::u_pattern_size_a,
uniforms::u_pattern_size_b,
+ uniforms::u_texsize,
uniforms::u_fade,
uniforms::u_image>,
- style::LinePaintProperties>
+ RenderLinePaintProperties>
{
public:
using Program::Program;
- static UniformValues uniformValues(const style::LinePaintProperties::Evaluated&,
+ static UniformValues uniformValues(const RenderLinePaintProperties::PossiblyEvaluated&,
const RenderTile&,
const TransformState&,
const std::array<float, 2>& pixelsToGLUnits,
- const SpriteAtlasElement& posA,
- const SpriteAtlasElement& posB);
+ Size atlasSize,
+ const ImagePosition& posA,
+ const ImagePosition& posB);
};
class LineSDFProgram : public Program<
@@ -136,7 +135,6 @@ class LineSDFProgram : public Program<
LineLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
- uniforms::u_width,
uniforms::u_ratio,
uniforms::u_gl_units_to_pixels,
uniforms::u_patternscale_a,
@@ -146,19 +144,18 @@ class LineSDFProgram : public Program<
uniforms::u_mix,
uniforms::u_sdfgamma,
uniforms::u_image>,
- style::LinePaintProperties>
+ RenderLinePaintProperties>
{
public:
using Program::Program;
- static UniformValues uniformValues(const style::LinePaintProperties::Evaluated&,
+ static UniformValues uniformValues(const RenderLinePaintProperties::PossiblyEvaluated&,
float pixelRatio,
const RenderTile&,
const TransformState&,
const std::array<float, 2>& pixelsToGLUnits,
const LinePatternPos& posA,
const LinePatternPos& posB,
- float dashLineWidth,
float atlasWidth);
};
diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp
index ca8434cf0a..bcdb270b9c 100644
--- a/src/mbgl/programs/program.hpp
+++ b/src/mbgl/programs/program.hpp
@@ -57,7 +57,7 @@ public:
const gl::IndexBuffer<DrawMode>& indexBuffer,
const SegmentVector<Attributes>& segments,
const PaintPropertyBinders& paintPropertyBinders,
- const typename PaintProperties::Evaluated& currentProperties,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
float currentZoom,
const std::string& layerID) {
typename AllUniforms::Values allUniformValues = uniformValues
@@ -101,7 +101,7 @@ public:
parameters(std::move(parameters_)) {
}
- Program& get(const typename PaintProperties::Evaluated& currentProperties) {
+ Program& get(const typename PaintProperties::PossiblyEvaluated& currentProperties) {
Bitset bits = PaintPropertyBinders::constants(currentProperties);
auto it = programs.find(bits);
if (it != programs.end()) {
diff --git a/src/mbgl/programs/segment.cpp b/src/mbgl/programs/segment.cpp
deleted file mode 100644
index bb09843e21..0000000000
--- a/src/mbgl/programs/segment.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <mbgl/programs/segment.hpp>
-
-namespace mbgl {
-namespace gl {
-
-} // namespace gl
-} // namespace mbgl
diff --git a/src/mbgl/programs/segment.hpp b/src/mbgl/programs/segment.hpp
index 937df4dece..f729683ac9 100644
--- a/src/mbgl/programs/segment.hpp
+++ b/src/mbgl/programs/segment.hpp
@@ -38,9 +38,6 @@ public:
};
template <class Attributes>
-class SegmentVector : public std::vector<Segment<Attributes>> {
-public:
- SegmentVector() = default;
-};
+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 86f61c4ad2..58174ff8a7 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,6 +34,7 @@ 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,
Args&&... args) {
@@ -45,18 +48,49 @@ 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{ std::array<float, 2> {{ float(texsize.width) / 4, float(texsize.height) / 4 }} },
- uniforms::u_zoom::Value{ float(state.getZoom()) },
- uniforms::u_rotate_with_map::Value{ values.rotationAlignment == AlignmentType::Map },
+ uniforms::u_texsize::Value{ texsize },
uniforms::u_texture::Value{ 0 },
uniforms::u_fadetexture::Value{ 1 },
uniforms::u_is_text::Value{ isText },
+ uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() },
+ uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() },
+ uniforms::u_pitch::Value{ state.getPitch() },
+ uniforms::u_pitch_with_map::Value{ pitchWithMap },
+ 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,6 +100,7 @@ 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)
{
@@ -74,6 +109,7 @@ SymbolIconProgram::uniformValues(const bool isText,
values,
texsize,
pixelsToGLUnits,
+ alongLine,
tile,
state
);
@@ -85,26 +121,24 @@ 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 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,
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 5fb0c4f15f..a7abf94f56 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -14,7 +14,7 @@
#include <mbgl/util/size.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <cmath>
@@ -30,12 +30,9 @@ class RenderTile;
class TransformState;
namespace uniforms {
-MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_texsize);
-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);
@@ -44,50 +41,60 @@ 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))
}},
{{
- static_cast<uint16_t>(tx / 4),
- static_cast<uint16_t>(ty / 4),
- 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)
- )
+ tx,
+ ty,
+ static_cast<uint16_t>(sizeData.min * 10),
+ static_cast<uint16_t>(sizeData.max * 10)
}}
};
}
};
+
+struct SymbolDynamicLayoutAttributes : gl::Attributes<attributes::a_projected_pos> {
+ static Vertex vertex(Point<float> anchorPoint, float labelAngle, float labelminzoom) {
+ return Vertex {
+ {{
+ anchorPoint.x,
+ anchorPoint.y,
+ static_cast<float>(mbgl::attributes::packUint8Pair(
+ static_cast<uint8_t>(std::fmod(labelAngle + 2 * M_PI, 2 * M_PI) / (2 * M_PI) * 255),
+ static_cast<uint8_t>(labelminzoom * 10)))
+ }}
+ };
+ }
+};
-class SymbolSizeAttributes : public gl::Attributes<attributes::a_size> {
-public:
- using Attribute = attributes::a_size::Type;
+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
@@ -100,18 +107,25 @@ 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;
+
+ 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 the smallest range of stops that covers the interval [lowerZoom, upperZoom]
@@ -123,7 +137,7 @@ Range<float> getCoveringStops(Stops s, float lowerZoom, float 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->first > lowerZoom) {
+ if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > lowerZoom) {
minIt--;
}
return Range<float> {
@@ -144,9 +158,10 @@ public:
: 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(
- getCoveringStops(stops, tileZoom, tileZoom + 1),
- Range<float> { function_.evaluate(tileZoom), function_.evaluate(tileZoom + 1) }
+ zoomLevels,
+ Range<float> { function_.evaluate(zoomLevels.min), function_.evaluate(zoomLevels.max) }
);
functionInterpolationBase = stops.base;
},
@@ -156,14 +171,9 @@ public:
);
}
- SymbolSizeAttributes::Bindings attributeBindings() const override {
- return SymbolSizeAttributes::Bindings { {} };
- }
-
- 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) {
@@ -182,14 +192,9 @@ public:
} 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;
@@ -211,49 +216,22 @@ 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;
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_),
@@ -264,51 +242,27 @@ public:
return getCoveringStops(stops, 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),
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;
const float defaultValue;
float layoutZoom;
Range<float> coveringZoomStops;
-
- VertexVector vertices;
- optional<VertexBuffer> buffer;
};
@@ -322,7 +276,7 @@ public:
using LayoutAttributes = LayoutAttrs;
using LayoutVertex = typename LayoutAttributes::Vertex;
- using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>;
+ using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolDynamicLayoutAttributes>;
using PaintProperties = PaintProps;
using PaintPropertyBinders = typename PaintProperties::Binders;
@@ -355,11 +309,12 @@ public:
gl::ColorMode colorMode,
const UniformValues& uniformValues,
const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
+ const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer,
const SymbolSizeBinder& symbolSizeBinder,
const gl::IndexBuffer<DrawMode>& indexBuffer,
const SegmentVector<Attributes>& segments,
const PaintPropertyBinders& paintPropertyBinders,
- const typename PaintProperties::Evaluated& currentProperties,
+ const typename PaintProperties::PossiblyEvaluated& currentProperties,
float currentZoom,
const std::string& layerID) {
typename AllUniforms::Values allUniformValues = uniformValues
@@ -367,7 +322,7 @@ public:
.concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer)
- .concat(symbolSizeBinder.attributeBindings())
+ .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer))
.concat(paintPropertyBinders.attributeBindings(currentProperties));
for (auto& segment : segments) {
@@ -399,13 +354,20 @@ 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_is_text,
+ uniforms::u_collision_y_stretch,
+ 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:
@@ -415,6 +377,7 @@ public:
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile&,
const TransformState&);
};
@@ -431,18 +394,21 @@ 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_is_text,
- uniforms::u_gamma_scale,
+ uniforms::u_collision_y_stretch,
+ 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>
{
@@ -452,18 +418,21 @@ 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_is_text,
- uniforms::u_gamma_scale,
+ uniforms::u_collision_y_stretch,
+ 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>;
@@ -477,6 +446,7 @@ public:
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile&,
const TransformState&,
const SymbolSDFPart);
diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp
index 32d857a94e..285d243251 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);
@@ -27,16 +28,21 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_halo_blur);
MBGL_DEFINE_UNIFORM_SCALAR(Color, u_outline_color);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_height);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_base);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_width);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_floorwidth);
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_VECTOR(float, 2, u_extrude_scale);
-MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_a);
-MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_br_a);
-MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_b);
-MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_br_b);
+MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_a);
+MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_a);
+MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_tl_b);
+MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, u_pattern_br_b);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_a);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_b);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_upper);
@@ -44,6 +50,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/map/backend_scope.cpp b/src/mbgl/renderer/backend_scope.cpp
index dac90346d7..fafeaabb39 100644
--- a/src/mbgl/map/backend_scope.cpp
+++ b/src/mbgl/renderer/backend_scope.cpp
@@ -1,5 +1,5 @@
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/map/backend.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
#include <mbgl/util/thread_local.hpp>
#include <cassert>
@@ -8,7 +8,7 @@ namespace mbgl {
static util::ThreadLocal<BackendScope> currentScope;
-BackendScope::BackendScope(Backend& backend_, ScopeType scopeType_)
+BackendScope::BackendScope(RendererBackend& backend_, ScopeType scopeType_)
: priorScope(currentScope.get()),
nextScope(nullptr),
backend(backend_),
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/bucket_parameters.hpp b/src/mbgl/renderer/bucket_parameters.hpp
index 1774ba2bbe..50ec4cf521 100644
--- a/src/mbgl/renderer/bucket_parameters.hpp
+++ b/src/mbgl/renderer/bucket_parameters.hpp
@@ -9,6 +9,7 @@ class BucketParameters {
public:
const OverscaledTileID tileID;
const MapMode mode;
+ const float pixelRatio;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp
index 1e08eca478..04126990b3 100644
--- a/src/mbgl/renderer/circle_bucket.cpp
+++ b/src/mbgl/renderer/buckets/circle_bucket.cpp
@@ -1,9 +1,8 @@
-#include <mbgl/renderer/circle_bucket.hpp>
+#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/render_circle_layer.hpp>
+#include <mbgl/renderer/layers/render_circle_layer.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/math.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();
}
diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp
index 0f27e2a7e3..78b6351bcb 100644
--- a/src/mbgl/renderer/circle_bucket.hpp
+++ b/src/mbgl/renderer/buckets/circle_bucket.hpp
@@ -23,8 +23,6 @@ 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;
diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp
index 2a514989cf..53c751c443 100644
--- a/src/mbgl/renderer/debug_bucket.cpp
+++ b/src/mbgl/renderer/buckets/debug_bucket.cpp
@@ -1,7 +1,7 @@
-#include <mbgl/renderer/debug_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/buckets/debug_bucket.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/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp
index fc3128e944..fc3128e944 100644
--- a/src/mbgl/renderer/debug_bucket.hpp
+++ b/src/mbgl/renderer/buckets/debug_bucket.hpp
diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp
index 2409fd365b..110db887a1 100644
--- a/src/mbgl/renderer/fill_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_bucket.cpp
@@ -1,9 +1,8 @@
-#include <mbgl/renderer/fill_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/buckets/fill_bucket.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
-#include <mbgl/renderer/render_fill_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_layer.hpp>
#include <mbgl/util/math.hpp>
#include <mapbox/earcut.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/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp
index d3cd92d451..a50e1971f5 100644
--- a/src/mbgl/renderer/fill_bucket.hpp
+++ b/src/mbgl/renderer/buckets/fill_bucket.hpp
@@ -23,7 +23,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;
diff --git a/src/mbgl/renderer/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
index 2b352ab66a..7f53326fe1 100644
--- a/src/mbgl/renderer/fill_extrusion_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
@@ -1,9 +1,8 @@
-#include <mbgl/renderer/fill_extrusion_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
#include <mbgl/programs/fill_extrusion_program.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
-#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/constants.hpp>
@@ -154,13 +153,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/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
index d1e695c5a3..d57265ab16 100644
--- a/src/mbgl/renderer/fill_extrusion_bucket.hpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
@@ -21,7 +21,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;
diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp
index c80b8900ea..a96518df38 100644
--- a/src/mbgl/renderer/line_bucket.cpp
+++ b/src/mbgl/renderer/buckets/line_bucket.cpp
@@ -1,6 +1,5 @@
-#include <mbgl/renderer/line_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/render_line_layer.hpp>
+#include <mbgl/renderer/buckets/line_bucket.hpp>
+#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
#include <mbgl/util/math.hpp>
@@ -14,9 +13,10 @@ using namespace style;
LineBucket::LineBucket(const BucketParameters& parameters,
const std::vector<const RenderLayer*>& layers,
- const style::LineLayoutProperties& layout_)
+ 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));
@@ -183,7 +186,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType
const bool isSharpCorner = cosHalfAngle < COS_HALF_SHARP_CORNER && prevCoordinate && nextCoordinate;
if (isSharpCorner && i > first) {
- const double prevSegmentLength = util::dist<double>(*currentCoordinate, *prevCoordinate);
+ const auto prevSegmentLength = util::dist<double>(*currentCoordinate, *prevCoordinate);
if (prevSegmentLength > 2.0 * sharpCornerOffset) {
GeometryCoordinate newPrevVertex = *currentCoordinate - convertPoint<int16_t>(util::round(convertPoint<double>(*currentCoordinate - *prevCoordinate) * (sharpCornerOffset / prevSegmentLength)));
distance += util::dist<double>(newPrevVertex, *prevCoordinate);
@@ -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) {
@@ -356,7 +359,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType
}
if (isSharpCorner && i < len - 1) {
- const double nextSegmentLength = util::dist<double>(*currentCoordinate, *nextCoordinate);
+ const auto nextSegmentLength = util::dist<double>(*currentCoordinate, *nextCoordinate);
if (nextSegmentLength > 2 * sharpCornerOffset) {
GeometryCoordinate newCurrentVertex = *currentCoordinate + convertPoint<int16_t>(util::round(convertPoint<double>(*nextCoordinate - *currentCoordinate) * (sharpCornerOffset / nextSegmentLength)));
distance += util::dist<double>(newCurrentVertex, *currentCoordinate);
@@ -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();
}
@@ -480,7 +476,7 @@ static float get(const RenderLineLayer& layer, const std::map<std::string, LineP
}
float LineBucket::getLineWidth(const RenderLineLayer& layer) const {
- float lineWidth = layer.evaluated.get<LineWidth>();
+ float lineWidth = get<LineWidth>(layer, paintPropertyBinders);
float gapWidth = get<LineGapWidth>(layer, paintPropertyBinders);
if (gapWidth) {
diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp
index 95ef2f9a6f..4fb77c377e 100644
--- a/src/mbgl/renderer/line_bucket.hpp
+++ b/src/mbgl/renderer/buckets/line_bucket.hpp
@@ -19,14 +19,13 @@ class LineBucket : public Bucket {
public:
LineBucket(const BucketParameters&,
const std::vector<const RenderLayer*>&,
- const style::LineLayoutProperties&);
+ const style::LineLayoutProperties::Unevaluated&);
void addFeature(const GeometryTileFeature&,
const GeometryCollection&) override;
bool hasData() const override;
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
@@ -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
new file mode 100644
index 0000000000..a66dd42d74
--- /dev/null
+++ b/src/mbgl/renderer/buckets/raster_bucket.cpp
@@ -0,0 +1,110 @@
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
+#include <mbgl/renderer/layers/render_raster_layer.hpp>
+#include <mbgl/programs/raster_program.hpp>
+#include <mbgl/gl/context.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+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);
+ }
+ if (!segments.empty()) {
+ vertexBuffer = context.createVertexBuffer(std::move(vertices));
+ indexBuffer = context.createIndexBuffer(std::move(indices));
+ }
+ uploaded = true;
+}
+
+void RasterBucket::clear() {
+ vertexBuffer = {};
+ indexBuffer = {};
+ segments.clear();
+ vertices.clear();
+ indices.clear();
+
+ uploaded = false;
+}
+
+void RasterBucket::setImage(std::shared_ptr<PremultipliedImage> image_) {
+ image = std::move(image_);
+ texture = {};
+ uploaded = false;
+}
+
+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 !!image;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp
new file mode 100644
index 0000000000..3800eadec8
--- /dev/null
+++ b/src/mbgl/renderer/buckets/raster_bucket.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/gl/texture.hpp>
+#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>
+
+namespace mbgl {
+
+class RasterBucket : public Bucket {
+public:
+ RasterBucket(PremultipliedImage&&);
+ RasterBucket(std::shared_ptr<PremultipliedImage>);
+
+ void upload(gl::Context&) override;
+ bool hasData() const override;
+
+ void clear();
+ 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;
+ SegmentVector<RasterAttributes> segments;
+
+ optional<gl::VertexBuffer<RasterLayoutVertex>> vertexBuffer;
+ optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index 9b016c16f9..a3f71f1f6e 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -1,8 +1,8 @@
-#include <mbgl/renderer/symbol_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
namespace mbgl {
@@ -10,7 +10,8 @@ using namespace style;
SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_,
const std::map<std::string, std::pair<
- style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties,
+ style::IconPaintProperties::PossiblyEvaluated,
+ style::TextPaintProperties::PossiblyEvaluated>>& layerPaintProperties,
const style::DataDrivenPropertyValue<float>& textSize,
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
@@ -36,14 +37,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
void SymbolBucket::upload(gl::Context& context) {
if (hasTextData()) {
text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
+ text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw);
text.indexBuffer = context.createIndexBuffer(std::move(text.triangles));
- textSizeBinder->upload(context);
}
if (hasIconData()) {
icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
+ icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw);
icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles));
- iconSizeBinder->upload(context);
}
if (!collisionBox.vertices.empty()) {
@@ -59,16 +60,8 @@ void SymbolBucket::upload(gl::Context& context) {
uploaded = true;
}
-void SymbolBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderSymbol(parameters, *this, *layer.as<RenderSymbolLayer>(), tile);
-}
-
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 {
diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index a9bc868db0..32f976bcb2 100644
--- a/src/mbgl/renderer/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -15,10 +15,27 @@
namespace mbgl {
+class PlacedSymbol {
+public:
+ PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
+ std::array<float, 2> lineOffset_, float placementZoom_, bool useVerticalMode_, GeometryCoordinates line_) :
+ anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
+ lineOffset(lineOffset_), placementZoom(placementZoom_), useVerticalMode(useVerticalMode_), line(std::move(line_)) {}
+ Point<float> anchorPoint;
+ uint16_t segment;
+ float lowerSize;
+ float upperSize;
+ std::array<float, 2> lineOffset;
+ float placementZoom;
+ bool useVerticalMode;
+ GeometryCoordinates line;
+ std::vector<float> glyphOffsets;
+};
+
class SymbolBucket : public Bucket {
public:
SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated,
- const std::map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&,
+ const std::map<std::string, std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>&,
const style::DataDrivenPropertyValue<float>& textSize,
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
@@ -26,7 +43,6 @@ public:
bool iconsNeedLinear);
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
bool hasData() const override;
bool hasTextData() const;
bool hasIconData() const;
@@ -44,10 +60,13 @@ public:
struct TextBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
gl::IndexVector<gl::Triangles> triangles;
SegmentVector<SymbolTextAttributes> segments;
+ std::vector<PlacedSymbol> placedSymbols;
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
+ optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
} text;
@@ -55,10 +74,14 @@ public:
struct IconBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
gl::IndexVector<gl::Triangles> triangles;
SegmentVector<SymbolIconAttributes> segments;
+ std::vector<PlacedSymbol> placedSymbols;
+ PremultipliedImage atlasImage;
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
+ optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
} icon;
@@ -68,10 +91,9 @@ public:
SegmentVector<CollisionBoxAttributes> segments;
optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer;
+ optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
optional<gl::IndexBuffer<gl::Lines>> indexBuffer;
} collisionBox;
-
- SpriteAtlas* spriteAtlas = nullptr;
};
} // 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/data_driven_property_evaluator.hpp b/src/mbgl/renderer/data_driven_property_evaluator.hpp
index 6406b3478b..79ecd0d495 100644
--- a/src/mbgl/renderer/data_driven_property_evaluator.hpp
+++ b/src/mbgl/renderer/data_driven_property_evaluator.hpp
@@ -24,12 +24,18 @@ public:
}
ResultType operator()(const style::CameraFunction<T>& function) const {
- return ResultType(function.evaluate(parameters.z));
+ if (!parameters.useIntegerZoom) {
+ return ResultType(function.evaluate(parameters.z));
+ } else {
+ return ResultType(function.evaluate(floor(parameters.z)));
+ }
}
template <class Function>
ResultType operator()(const Function& function) const {
- return ResultType(function);
+ auto returnFunction = function;
+ returnFunction.useIntegerZoom = parameters.useIntegerZoom;
+ return ResultType(returnFunction);
}
private:
diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp
index 35e246f488..de153b6963 100644
--- a/src/mbgl/renderer/frame_history.cpp
+++ b/src/mbgl/renderer/frame_history.cpp
@@ -37,9 +37,9 @@ void FrameHistory::record(const TimePoint& now, float zoom, const Duration& dura
}
for (int16_t z = 0; z <= 255; z++) {
- std::chrono::duration<float> timeDiff = now - changeTimes[z];
- int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255;
- uint8_t opacity = z <= zoomIndex
+ 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) {
@@ -74,4 +74,8 @@ void FrameHistory::bind(gl::Context& context, uint32_t unit) {
context.bindTexture(*texture, unit);
}
+bool FrameHistory::isVisible(const float zoom) const {
+ return opacities.data[std::floor(zoom * 10)] != 0;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp
index f2b11f5f41..75a8b60a71 100644
--- a/src/mbgl/renderer/frame_history.hpp
+++ b/src/mbgl/renderer/frame_history.hpp
@@ -22,6 +22,7 @@ public:
bool needsAnimation(const Duration&) const;
void bind(gl::Context&, uint32_t);
void upload(gl::Context&, uint32_t);
+ bool isVisible(const float zoom) const;
private:
std::array<TimePoint, 256> changeTimes;
diff --git a/src/mbgl/renderer/group_by_layout.cpp b/src/mbgl/renderer/group_by_layout.cpp
index df1eb7c7dd..3b02727ff8 100644
--- a/src/mbgl/renderer/group_by_layout.cpp
+++ b/src/mbgl/renderer/group_by_layout.cpp
@@ -19,13 +19,13 @@ std::string layoutKey(const RenderLayer& layer) {
writer.StartArray();
writer.Uint(static_cast<uint32_t>(layer.type));
- writer.String(layer.baseImpl.source);
- writer.String(layer.baseImpl.sourceLayer);
- writer.Double(layer.baseImpl.minZoom);
- writer.Double(layer.baseImpl.maxZoom);
- writer.Uint(static_cast<uint32_t>(layer.baseImpl.visibility));
- stringify(writer, layer.baseImpl.filter);
- layer.baseImpl.stringifyLayout(writer);
+ writer.String(layer.baseImpl->source);
+ writer.String(layer.baseImpl->sourceLayer);
+ writer.Double(layer.baseImpl->minZoom);
+ writer.Double(layer.baseImpl->maxZoom);
+ writer.Uint(static_cast<uint32_t>(layer.baseImpl->visibility));
+ stringify(writer, layer.baseImpl->filter);
+ layer.baseImpl->stringifyLayout(writer);
writer.EndArray();
return s.GetString();
diff --git a/src/mbgl/renderer/image_atlas.cpp b/src/mbgl/renderer/image_atlas.cpp
new file mode 100644
index 0000000000..8eee7c2095
--- /dev/null
+++ b/src/mbgl/renderer/image_atlas.cpp
@@ -0,0 +1,60 @@
+#include <mbgl/renderer/image_atlas.hpp>
+
+#include <mapbox/shelf-pack.hpp>
+
+namespace mbgl {
+
+static constexpr uint32_t padding = 1;
+
+ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& image)
+ : pixelRatio(image.pixelRatio),
+ textureRect(
+ bin.x + padding,
+ bin.y + padding,
+ bin.w - padding * 2,
+ bin.h - padding * 2
+ ) {
+}
+
+ImageAtlas makeImageAtlas(const ImageMap& images) {
+ ImageAtlas result;
+
+ mapbox::ShelfPack::ShelfPackOptions options;
+ options.autoResize = true;
+ mapbox::ShelfPack pack(0, 0, options);
+
+ for (const auto& entry : images) {
+ const style::Image::Impl& image = *entry.second;
+
+ const mapbox::Bin& bin = *pack.packOne(-1,
+ image.image.size.width + 2 * padding,
+ image.image.size.height + 2 * padding);
+
+ result.image.resize({
+ static_cast<uint32_t>(pack.width()),
+ static_cast<uint32_t>(pack.height())
+ });
+
+ PremultipliedImage::copy(image.image,
+ result.image,
+ { 0, 0 },
+ {
+ bin.x + padding,
+ bin.y + padding
+ },
+ image.image.size);
+
+ result.positions.emplace(image.id,
+ ImagePosition { bin, image });
+ }
+
+ pack.shrink();
+ result.image.resize({
+ static_cast<uint32_t>(pack.width()),
+ static_cast<uint32_t>(pack.height())
+ });
+
+ return result;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/image_atlas.hpp b/src/mbgl/renderer/image_atlas.hpp
new file mode 100644
index 0000000000..b3cc166eff
--- /dev/null
+++ b/src/mbgl/renderer/image_atlas.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/util/rect.hpp>
+
+#include <mapbox/shelf-pack.hpp>
+
+#include <array>
+
+namespace mbgl {
+
+class ImagePosition {
+public:
+ ImagePosition(const mapbox::Bin&, const style::Image::Impl&);
+
+ float pixelRatio;
+ Rect<uint16_t> textureRect;
+
+ std::array<uint16_t, 2> tl() const {
+ return {{
+ textureRect.x,
+ textureRect.y
+ }};
+ }
+
+ std::array<uint16_t, 2> br() const {
+ return {{
+ static_cast<uint16_t>(textureRect.x + textureRect.w),
+ static_cast<uint16_t>(textureRect.y + textureRect.h)
+ }};
+ }
+
+ std::array<float, 2> displaySize() const {
+ return {{
+ textureRect.w / pixelRatio,
+ textureRect.h / pixelRatio,
+ }};
+ }
+};
+
+using ImagePositions = std::map<std::string, ImagePosition>;
+
+class ImageAtlas {
+public:
+ PremultipliedImage image;
+ ImagePositions positions;
+};
+
+ImageAtlas makeImageAtlas(const ImageMap&);
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp
new file mode 100644
index 0000000000..2ef6be0c4f
--- /dev/null
+++ b/src/mbgl/renderer/image_manager.cpp
@@ -0,0 +1,184 @@
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/gl/context.hpp>
+
+namespace mbgl {
+
+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();
+ }
+}
+
+bool ImageManager::isLoaded() const {
+ return loaded;
+}
+
+void ImageManager::addImage(Immutable<style::Image::Impl> image_) {
+ assert(images.find(image_->id) == images.end());
+ images.emplace(image_->id, std::move(image_));
+}
+
+void ImageManager::updateImage(Immutable<style::Image::Impl> image_) {
+ removeImage(image_->id);
+ addImage(std::move(image_));
+}
+
+void ImageManager::removeImage(const std::string& id) {
+ assert(images.find(id) != images.end());
+ images.erase(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);
+ }
+}
+
+const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
+ const auto it = images.find(id);
+ if (it != images.end()) {
+ return it->second.get();
+ }
+ return nullptr;
+}
+
+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 : pair.first) {
+ if (images.find(dependency) == images.end()) {
+ hasAllDependencies = false;
+ }
+ }
+ }
+ if (isLoaded() || hasAllDependencies) {
+ notify(requestor, std::move(pair));
+ } else {
+ requestors.emplace(&requestor, std::move(pair));
+ }
+}
+
+void ImageManager::removeRequestor(ImageRequestor& requestor) {
+ requestors.erase(&requestor);
+}
+
+void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const {
+ ImageMap response;
+
+ for (const auto& dependency : pair.first) {
+ auto it = images.find(dependency);
+ if (it != images.end()) {
+ response.emplace(*it);
+ }
+ }
+
+ requestor.onImagesAvailable(response, pair.second);
+}
+
+void ImageManager::dumpDebugLogs() const {
+ Log::Info(Event::General, "ImageManager::loaded: %d", loaded);
+}
+
+// When copied into the atlas texture, image data is padded by one pixel on each side. Icon
+// images are padded with fully transparent pixels, while pattern images are padded with a
+// copy of the image data wrapped from the opposite side. In both cases, this ensures the
+// correct behavior of GL_LINEAR texture sampling mode.
+static constexpr uint16_t padding = 1;
+
+static mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() {
+ mapbox::ShelfPack::ShelfPackOptions options;
+ options.autoResize = true;
+ return options;
+}
+
+ImageManager::ImageManager()
+ : shelfPack(64, 64, shelfPackOptions()) {
+}
+
+ImageManager::~ImageManager() = default;
+
+optional<ImagePosition> ImageManager::getPattern(const std::string& id) {
+ auto it = patterns.find(id);
+ if (it != patterns.end()) {
+ return it->second.position;
+ }
+
+ const style::Image::Impl* image = getImage(id);
+ if (!image) {
+ return {};
+ }
+
+ const uint16_t width = image->image.size.width + padding * 2;
+ const uint16_t height = image->image.size.height + padding * 2;
+
+ mapbox::Bin* bin = shelfPack.packOne(-1, width, height);
+ if (!bin) {
+ return {};
+ }
+
+ atlasImage.resize(getPixelSize());
+
+ const PremultipliedImage& src = image->image;
+
+ const uint32_t x = bin->x + padding;
+ const uint32_t y = bin->y + padding;
+ const uint32_t w = src.size.width;
+ const uint32_t h = src.size.height;
+
+ PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h });
+
+ // Add 1 pixel wrapped padding on each side of the image.
+ PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T
+ PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B
+ PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L
+ PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R
+
+ dirty = true;
+
+ return patterns.emplace(id, Pattern { bin, { *bin, *image } }).first->second.position;
+}
+
+Size ImageManager::getPixelSize() const {
+ return Size {
+ static_cast<uint32_t>(shelfPack.width()),
+ static_cast<uint32_t>(shelfPack.height())
+ };
+}
+
+void ImageManager::upload(gl::Context& context, gl::TextureUnit unit) {
+ if (!atlasTexture) {
+ atlasTexture = context.createTexture(atlasImage, unit);
+ } else if (dirty) {
+ context.updateTexture(*atlasTexture, atlasImage, unit);
+ }
+
+ dirty = false;
+}
+
+void ImageManager::bind(gl::Context& context, gl::TextureUnit unit) {
+ upload(context, unit);
+ context.bindTexture(*atlasTexture, unit, gl::TextureFilter::Linear);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp
new file mode 100644
index 0000000000..f72ba9fb53
--- /dev/null
+++ b/src/mbgl/renderer/image_manager.hpp
@@ -0,0 +1,91 @@
+#pragma once
+
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/immutable.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/gl/texture.hpp>
+
+#include <mapbox/shelf-pack.hpp>
+
+#include <set>
+#include <string>
+
+namespace mbgl {
+
+namespace gl {
+class Context;
+} // namespace gl
+
+class ImageRequestor {
+public:
+ virtual ~ImageRequestor() = default;
+ virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0;
+};
+
+/*
+ ImageManager does two things:
+
+ 1. Tracks requests for icon images from tile workers and sends responses when the requests are fulfilled.
+ 2. Builds a texture atlas for pattern images.
+
+ These are disparate responsibilities and should eventually be handled by different classes. When we implement
+ data-driven support for `*-pattern`, we'll likely use per-bucket pattern atlases, and that would be a good time
+ to refactor this.
+*/
+class ImageManager : public util::noncopyable {
+public:
+ ImageManager();
+ ~ImageManager();
+
+ void setLoaded(bool);
+ bool isLoaded() const;
+
+ void dumpDebugLogs() const;
+
+ const style::Image::Impl* getImage(const std::string&) const;
+
+ void addImage(Immutable<style::Image::Impl>);
+ void updateImage(Immutable<style::Image::Impl>);
+ void removeImage(const std::string&);
+
+ void getImages(ImageRequestor&, ImageRequestPair&&);
+ void removeRequestor(ImageRequestor&);
+
+private:
+ void notify(ImageRequestor&, const ImageRequestPair&) const;
+
+ bool loaded = false;
+
+ std::unordered_map<ImageRequestor*, ImageRequestPair> requestors;
+ ImageMap images;
+
+// Pattern stuff
+public:
+ optional<ImagePosition> getPattern(const std::string& name);
+
+ void bind(gl::Context&, gl::TextureUnit unit);
+ void upload(gl::Context&, gl::TextureUnit unit);
+
+ Size getPixelSize() const;
+
+ // Only for use in tests.
+ const PremultipliedImage& getAtlasImage() const {
+ return atlasImage;
+ }
+
+private:
+ struct Pattern {
+ mapbox::Bin* bin;
+ ImagePosition position;
+ };
+
+ mapbox::ShelfPack shelfPack;
+ std::unordered_map<std::string, Pattern> patterns;
+ PremultipliedImage atlasImage;
+ mbgl::optional<gl::Texture> atlasTexture;
+ bool dirty = true;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp
new file mode 100644
index 0000000000..9fddba3f74
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -0,0 +1,115 @@
+#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()) {
+}
+
+const style::BackgroundLayer::Impl& RenderBackgroundLayer::impl() const {
+ return static_cast<const style::BackgroundLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &,
+ const std::vector<const RenderLayer *> &) const {
+ assert(false);
+ return nullptr;
+}
+
+void RenderBackgroundLayer::transition(const TransitionParameters &parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters &parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent
+ : RenderPass::None;
+}
+
+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/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp
index b1f709953f..a619670ee4 100644
--- a/src/mbgl/renderer/render_background_layer.hpp
+++ b/src/mbgl/renderer/layers/render_background_layer.hpp
@@ -1,30 +1,28 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/background_layer_impl.hpp>
#include <mbgl/style/layers/background_layer_properties.hpp>
namespace mbgl {
class RenderBackgroundLayer: public RenderLayer {
public:
-
- RenderBackgroundLayer(const style::BackgroundLayer::Impl&);
+ RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl>);
~RenderBackgroundLayer() final = default;
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) override;
+ 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;
// Paint properties
style::BackgroundPaintProperties::Unevaluated unevaluated;
- style::BackgroundPaintProperties::Evaluated evaluated;
+ style::BackgroundPaintProperties::PossiblyEvaluated evaluated;
- const style::BackgroundLayer::Impl* const impl;
+ const style::BackgroundLayer::Impl& impl() const;
};
template <>
@@ -32,4 +30,4 @@ inline bool RenderLayer::is<RenderBackgroundLayer>() const {
return type == style::LayerType::Background;
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp
new file mode 100644
index 0000000000..e7b022f3ee
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_circle_layer.cpp
@@ -0,0 +1,120 @@
+#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>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Circle, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::CircleLayer::Impl& RenderCircleLayer::impl() const {
+ return static_cast<const style::CircleLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<CircleBucket>(parameters, layers);
+}
+
+void RenderCircleLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 ||
+ evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0)
+ && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 ||
+ evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0)
+ && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 ||
+ evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0))
+ ? RenderPass::Translucent : RenderPass::None;
+}
+
+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::Still
+ ? 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,
+ const float zoom,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ // Translate query geometry
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::CircleTranslate>(),
+ evaluated.get<style::CircleTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ // Evaluate function
+ auto circleRadius = evaluated.get<style::CircleRadius>()
+ .evaluate(feature, zoom, style::CircleRadius::defaultValue())
+ * pixelsToTileUnits;
+
+ // Test intersection
+ return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp
index 3b82b5c988..f31715f98f 100644
--- a/src/mbgl/renderer/render_circle_layer.hpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.hpp
@@ -1,22 +1,20 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/circle_layer_impl.hpp>
#include <mbgl/style/layers/circle_layer_properties.hpp>
namespace mbgl {
class RenderCircleLayer: public RenderLayer {
public:
-
- RenderCircleLayer(const style::CircleLayer::Impl&);
+ RenderCircleLayer(Immutable<style::CircleLayer::Impl>);
~RenderCircleLayer() final = default;
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) override;
+ void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
@@ -29,9 +27,9 @@ public:
// Paint properties
style::CirclePaintProperties::Unevaluated unevaluated;
- style::CirclePaintProperties::Evaluated evaluated;
+ style::CirclePaintProperties::PossiblyEvaluated evaluated;
- const style::CircleLayer::Impl* const impl;
+ const style::CircleLayer::Impl& impl() const;
};
template <>
@@ -39,4 +37,4 @@ inline bool RenderLayer::is<RenderCircleLayer>() const {
return type == style::LayerType::Circle;
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp
new file mode 100644
index 0000000000..7ece3970da
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -0,0 +1,81 @@
+#include <mbgl/renderer/layers/render_custom_layer.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>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
+ : RenderLayer(LayerType::Custom, _impl) {
+}
+
+RenderCustomLayer::~RenderCustomLayer() {
+ assert(BackendScope::exists());
+ if (initialized) {
+ if (contextDestroyed && impl().contextLostFn ) {
+ impl().contextLostFn(impl().context);
+ } else if (!contextDestroyed && impl().deinitializeFn) {
+ impl().deinitializeFn(impl().context);
+ }
+ }
+}
+
+const CustomLayer::Impl& RenderCustomLayer::impl() const {
+ return static_cast<const CustomLayer::Impl&>(*baseImpl);
+}
+
+void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) {
+ passes = RenderPass::Translucent;
+}
+
+bool RenderCustomLayer::hasTransition() const {
+ return false;
+}
+
+std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
+ assert(false);
+ return nullptr;
+}
+
+void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) {
+ if (!initialized) {
+ assert(impl().initializeFn);
+ impl().initializeFn(impl().context);
+ initialized = true;
+ }
+
+ 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.bindVertexArray = 0;
+ context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly));
+ context.setStencilMode(gl::StencilMode::disabled());
+ context.setColorMode(paintParameters.colorModeForRenderPass());
+
+ CustomLayerRenderParameters parameters;
+
+ parameters.width = state.getSize().width;
+ parameters.height = state.getSize().height;
+ parameters.latitude = state.getLatLng().latitude();
+ parameters.longitude = state.getLatLng().longitude();
+ parameters.zoom = state.getZoom();
+ parameters.bearing = -state.getAngle() * util::RAD2DEG;
+ parameters.pitch = state.getPitch();
+ parameters.fieldOfView = state.getFieldOfView();
+
+ assert(impl().renderFn);
+ impl().renderFn(impl().context, parameters);
+
+ // Reset the view back to our original one, just in case the CustomLayer changed
+ // the viewport or Framebuffer.
+ paintParameters.backend.bind();
+ context.setDirtyState();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp
index c3af6c77b2..32ed9da8da 100644
--- a/src/mbgl/renderer/render_custom_layer.hpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.hpp
@@ -1,25 +1,31 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/style/layers/custom_layer_impl.hpp>
namespace mbgl {
class RenderCustomLayer: public RenderLayer {
public:
+ RenderCustomLayer(Immutable<style::CustomLayer::Impl>);
+ ~RenderCustomLayer() final;
- RenderCustomLayer(const style::CustomLayer::Impl&);
- ~RenderCustomLayer() final = default;
-
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) final {}
+ void transition(const TransitionParameters&) final {}
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final;
+ void render(PaintParameters&, RenderSource*) final;
+
+ const style::CustomLayer::Impl& impl() const;
- const style::CustomLayer::Impl* const impl;
+ void markContextDestroyed() {
+ contextDestroyed = true;
+ };
+
+private:
+ bool initialized = false;
+ bool contextDestroyed = false;
};
template <>
@@ -27,4 +33,4 @@ inline bool RenderLayer::is<RenderCustomLayer>() const {
return type == style::LayerType::Custom;
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
new file mode 100644
index 0000000000..fbd6160e8a
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -0,0 +1,167 @@
+#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>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::FillExtrusion, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::FillExtrusionLayer::Impl& RenderFillExtrusionLayer::impl() const {
+ return static_cast<const style::FillExtrusionLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<FillExtrusionBucket>(parameters, layers);
+}
+
+void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ 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,
+ const float,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::FillExtrusionTranslate>(),
+ evaluated.get<style::FillExtrusionTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
index bd66d8e3b1..838494cf91 100644
--- a/src/mbgl/renderer/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -1,22 +1,22 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/style/layers/fill_extrusion_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 {
class RenderFillExtrusionLayer: public RenderLayer {
public:
-
- RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl&);
+ RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl>);
~RenderFillExtrusionLayer() final = default;
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) override;
+ void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
@@ -29,9 +29,11 @@ public:
// Paint properties
style::FillExtrusionPaintProperties::Unevaluated unevaluated;
- style::FillExtrusionPaintProperties::Evaluated evaluated;
+ style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;
+
+ const style::FillExtrusionLayer::Impl& impl() const;
- const style::FillExtrusionLayer::Impl* const impl;
+ optional<OffscreenTexture> renderTexture;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
new file mode 100644
index 0000000000..22cb9563c1
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -0,0 +1,205 @@
+#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>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Fill, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::FillLayer::Impl& RenderFillLayer::impl() const {
+ return static_cast<const style::FillLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<FillBucket>(parameters, layers);
+}
+
+void RenderFillLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ if (unevaluated.get<style::FillOutlineColor>().isUndefined()) {
+ evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>();
+ }
+
+ passes = RenderPass::None;
+
+ if (evaluated.get<style::FillAntialias>()) {
+ passes |= RenderPass::Translucent;
+ }
+
+ if (!unevaluated.get<style::FillPattern>().isUndefined()
+ || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f
+ || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) {
+ passes |= RenderPass::Translucent;
+ } else {
+ passes |= RenderPass::Opaque;
+ }
+}
+
+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,
+ const float,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::FillTranslate>(),
+ evaluated.get<style::FillTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
+}
+
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp
index 8080cf289b..a51865698f 100644
--- a/src/mbgl/renderer/render_fill_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.hpp
@@ -1,22 +1,20 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/style/layers/fill_layer_properties.hpp>
namespace mbgl {
class RenderFillLayer: public RenderLayer {
public:
-
- RenderFillLayer(const style::FillLayer::Impl&);
+ RenderFillLayer(Immutable<style::FillLayer::Impl>);
~RenderFillLayer() final = default;
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) override;
+ void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
@@ -29,9 +27,9 @@ public:
// Paint properties
style::FillPaintProperties::Unevaluated unevaluated;
- style::FillPaintProperties::Evaluated evaluated;
+ style::FillPaintProperties::PossiblyEvaluated evaluated;
- const style::FillLayer::Impl* const impl;
+ const style::FillLayer::Impl& impl() const;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
new file mode 100644
index 0000000000..1b4a1c0ff7
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -0,0 +1,204 @@
+#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>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Line, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::LineLayer::Impl& RenderLineLayer::impl() const {
+ return static_cast<const style::LineLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<LineBucket>(parameters, layers, impl().layout);
+}
+
+void RenderLineLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ style::Properties<LineFloorwidth>::Unevaluated extra(unevaluated.get<style::LineWidth>());
+
+ auto dashArrayParams = parameters;
+ dashArrayParams.useIntegerZoom = true;
+
+ evaluated = RenderLinePaintProperties::PossiblyEvaluated(
+ unevaluated.evaluate(parameters).concat(extra.evaluate(dashArrayParams)));
+
+ passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0
+ && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0
+ && evaluated.get<style::LineWidth>().constantOr(1.0) > 0)
+ ? RenderPass::Translucent : RenderPass::None;
+}
+
+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 {};
+
+ GeometryCollection newRings;
+ Point<double> zero(0, 0);
+ for (const auto& ring : rings) {
+ newRings.emplace_back();
+ auto& newRing = newRings.back();
+
+ for (auto i = ring.begin(); i != ring.end(); i++) {
+ auto& p = *i;
+
+ Point<double> aToB = i == ring.begin() ?
+ zero :
+ util::perp(util::unit(convertPoint<double>(p - *(i - 1))));
+ Point<double> bToC = i + 1 == ring.end() ?
+ zero :
+ util::perp(util::unit(convertPoint<double>(*(i + 1) - p)));
+ Point<double> extrude = util::unit(aToB + bToC);
+
+ const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y;
+ extrude *= (1.0 / cosHalfAngle);
+
+ newRing.push_back(convertPoint<int16_t>(extrude * offset) + p);
+ }
+ }
+
+ return newRings;
+}
+
+bool RenderLineLayer::queryIntersectsFeature(
+ const GeometryCoordinates& queryGeometry,
+ const GeometryTileFeature& feature,
+ const float zoom,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ // Translate query geometry
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::LineTranslate>(),
+ evaluated.get<style::LineTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ // Evaluate function
+ auto offset = evaluated.get<style::LineOffset>()
+ .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits;
+
+ // Apply offset to geometry
+ auto offsetGeometry = offsetLine(feature.getGeometries(), offset);
+
+ // Test intersection
+ const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits;
+ return util::polygonIntersectsBufferedMultiLine(
+ translatedQueryGeometry.value_or(queryGeometry),
+ offsetGeometry.value_or(feature.getGeometries()),
+ halfWidth);
+}
+
+float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const {
+ float lineWidth = evaluated.get<style::LineWidth>()
+ .evaluate(feature, zoom, style::LineWidth::defaultValue());
+ float gapWidth = evaluated.get<style::LineGapWidth>()
+ .evaluate(feature, zoom, style::LineGapWidth::defaultValue());
+ if (gapWidth) {
+ return gapWidth + 2 * lineWidth;
+ } else {
+ return lineWidth;
+ }
+}
+
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp
index 6d6fecc227..8bf7e2329d 100644
--- a/src/mbgl/renderer/render_line_layer.hpp
+++ b/src/mbgl/renderer/layers/render_line_layer.hpp
@@ -1,22 +1,29 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/line_layer_impl.hpp>
#include <mbgl/style/layers/line_layer_properties.hpp>
+#include <mbgl/programs/uniforms.hpp>
namespace mbgl {
+struct LineFloorwidth : style::DataDrivenPaintProperty<float, attributes::a_floorwidth, uniforms::u_floorwidth> {
+ static float defaultValue() { return 1; }
+};
+
+class RenderLinePaintProperties : public style::ConcatenateProperties<
+ style::LinePaintProperties::PropertyTypes,
+ TypeList<LineFloorwidth>>::Type {};
+
class RenderLineLayer: public RenderLayer {
public:
-
- RenderLineLayer(const style::LineLayer::Impl&);
+ RenderLineLayer(Immutable<style::LineLayer::Impl>);
~RenderLineLayer() final = default;
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) override;
+ void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
@@ -29,16 +36,12 @@ public:
// Paint properties
style::LinePaintProperties::Unevaluated unevaluated;
- style::LinePaintProperties::Evaluated evaluated;
-
- const style::LineLayer::Impl* const impl;
+ RenderLinePaintProperties::PossiblyEvaluated evaluated;
- // Special case
- float dashLineWidth = 1;
+ const style::LineLayer::Impl& impl() const;
private:
float getLineWidth(const GeometryTileFeature&, const float) const;
-
};
template <>
diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp
new file mode 100644
index 0000000000..06616d90e5
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -0,0 +1,155 @@
+#include <mbgl/renderer/layers/render_raster_layer.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/sources/render_image_source.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()) {
+}
+
+const style::RasterLayer::Impl& RenderRasterLayer::impl() const {
+ return static_cast<const style::RasterLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
+ assert(false);
+ return nullptr;
+}
+
+void RenderRasterLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None;
+}
+
+bool RenderRasterLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+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);
+ }
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp
index 3ffeb8febf..87de316f7c 100644
--- a/src/mbgl/renderer/render_raster_layer.hpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.hpp
@@ -1,30 +1,29 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/raster_layer_impl.hpp>
#include <mbgl/style/layers/raster_layer_properties.hpp>
namespace mbgl {
class RenderRasterLayer: public RenderLayer {
public:
-
- RenderRasterLayer(const style::RasterLayer::Impl&);
+ RenderRasterLayer(Immutable<style::RasterLayer::Impl>);
~RenderRasterLayer() final = default;
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) override;
+ 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;
// Paint properties
style::RasterPaintProperties::Unevaluated unevaluated;
- style::RasterPaintProperties::Evaluated evaluated;
+ style::RasterPaintProperties::PossiblyEvaluated evaluated;
- const style::RasterLayer::Impl* const impl;
+ const style::RasterLayer::Impl& impl() const;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
new file mode 100644
index 0000000000..1376e8a3d8
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -0,0 +1,325 @@
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/bucket_parameters.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/frame_history.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/symbol_program.hpp>
+#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()) {
+}
+
+const style::SymbolLayer::Impl& RenderSymbolLayer::impl() const {
+ return static_cast<const style::SymbolLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
+ assert(false); // Should be calling createLayout() instead.
+ return nullptr;
+}
+
+std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters,
+ const std::vector<const RenderLayer*>& group,
+ std::unique_ptr<GeometryTileLayer> layer,
+ GlyphDependencies& glyphDependencies,
+ ImageDependencies& imageDependencies) const {
+ return std::make_unique<SymbolLayout>(parameters,
+ group,
+ std::move(layer),
+ imageDependencies,
+ glyphDependencies);
+}
+
+void RenderSymbolLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 ||
+ evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0;
+ auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 ||
+ evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0;
+
+ passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0)
+ || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0))
+ ? RenderPass::Translucent : RenderPass::None;
+}
+
+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;
+
+ parameters.frameHistory.bind(parameters.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 = parameters.mapMode == MapMode::Still;
+
+ program.get(paintProperties).draw(
+ parameters.context,
+ gl::Triangles(),
+ values_.pitchAlignment == AlignmentType::Map
+ ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)
+ : gl::DepthMode::disabled(),
+ needsClipping
+ ? parameters.stencilModeForClipping(tile.clip)
+ : gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ std::move(uniformValues),
+ *buffers.vertexBuffer,
+ *buffers.dynamicVertexBuffer,
+ *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.frameHistory);
+
+ 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, 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, 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),
+ 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.frameHistory);
+
+ 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, 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, 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);
+
+ parameters.programs.collisionBox.draw(
+ parameters.context,
+ gl::Lines { 1.0f },
+ gl::DepthMode::disabled(),
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ CollisionBoxProgram::UniformValues {
+ uniforms::u_matrix::Value{ tile.matrix },
+ uniforms::u_scale::Value{ std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)) },
+ uniforms::u_zoom::Value{ float(parameters.state.getZoom() * 10) },
+ uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) },
+ uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() },
+ uniforms::u_pitch::Value{ parameters.state.getPitch() },
+ uniforms::u_fadetexture::Value{ 1 }
+ },
+ *bucket.collisionBox.vertexBuffer,
+ *bucket.collisionBox.indexBuffer,
+ bucket.collisionBox.segments,
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ }
+}
+
+style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const {
+ return style::IconPaintProperties::PossiblyEvaluated {
+ evaluated.get<style::IconOpacity>(),
+ evaluated.get<style::IconColor>(),
+ evaluated.get<style::IconHaloColor>(),
+ evaluated.get<style::IconHaloWidth>(),
+ evaluated.get<style::IconHaloBlur>(),
+ evaluated.get<style::IconTranslate>(),
+ evaluated.get<style::IconTranslateAnchor>()
+ };
+}
+
+style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProperties() const {
+ return style::TextPaintProperties::PossiblyEvaluated {
+ evaluated.get<style::TextOpacity>(),
+ evaluated.get<style::TextColor>(),
+ evaluated.get<style::TextHaloColor>(),
+ evaluated.get<style::TextHaloWidth>(),
+ evaluated.get<style::TextHaloBlur>(),
+ evaluated.get<style::TextTranslate>(),
+ evaluated.get<style::TextTranslateAnchor>()
+ };
+}
+
+
+style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
+ return style::SymbolPropertyValues {
+ 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,
+ 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,
+ limitMaxDistance ? 1.5f : 10.0f
+ };
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp
index 80ffd95a06..83709b5122 100644
--- a/src/mbgl/renderer/render_symbol_layer.hpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp
@@ -2,8 +2,8 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
namespace mbgl {
@@ -13,7 +13,7 @@ namespace style {
// {icon,text}-specific paint-property packs for use in the symbol Programs.
// Since each program deals either with icons or text, using a smaller property set
// lets us avoid unnecessarily binding attributes for properties the program wouldn't use.
-class IconPaintProperties : public PaintProperties<
+class IconPaintProperties : public Properties<
IconOpacity,
IconColor,
IconHaloColor,
@@ -23,7 +23,7 @@ class IconPaintProperties : public PaintProperties<
IconTranslateAnchor
> {};
-class TextPaintProperties : public PaintProperties<
+class TextPaintProperties : public Properties<
TextOpacity,
TextColor,
TextHaloColor,
@@ -40,17 +40,16 @@ public:
// Layout
AlignmentType pitchAlignment;
AlignmentType rotationAlignment;
- PossiblyEvaluatedPropertyValue<float> layoutSize;
+ bool keepUpright;
// Paint
std::array<float, 2> translate;
TranslateAnchorType translateAnchor;
- float paintSize;
-
- float sdfScale; // Constant (1.0 or 24.0)
bool hasHalo;
bool hasFill;
+
+ float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else
};
} // namespace style
@@ -61,33 +60,35 @@ class GeometryTileLayer;
class RenderSymbolLayer: public RenderLayer {
public:
- RenderSymbolLayer(const style::SymbolLayer::Impl&);
+ RenderSymbolLayer(Immutable<style::SymbolLayer::Impl>);
~RenderSymbolLayer() final = default;
- std::unique_ptr<RenderLayer> clone() const override;
-
- void cascade(const CascadeParameters&) override;
+ void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
- style::IconPaintProperties::Evaluated iconPaintProperties() const;
- style::TextPaintProperties::Evaluated textPaintProperties() const;
+ style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const;
+ style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const;
style::SymbolPropertyValues iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const;
style::SymbolPropertyValues textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
- std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, const std::vector<const RenderLayer*>&,
- const GeometryTileLayer&, GlyphDependencies&, IconDependencies&) const;
+ std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&,
+ const std::vector<const RenderLayer*>&,
+ std::unique_ptr<GeometryTileLayer>,
+ GlyphDependencies&,
+ ImageDependencies&) const;
// Paint properties
style::SymbolPaintProperties::Unevaluated unevaluated;
- style::SymbolPaintProperties::Evaluated evaluated;
+ style::SymbolPaintProperties::PossiblyEvaluated evaluated;
float iconSize = 1.0f;
float textSize = 16.0f;
- const style::SymbolLayer::Impl* const impl;
+ const style::SymbolLayer::Impl& impl() const;
};
template <>
diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp
new file mode 100644
index 0000000000..299db844bc
--- /dev/null
+++ b/src/mbgl/renderer/paint_parameters.cpp
@@ -0,0 +1,98 @@
+#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_,
+ FrameHistory& frameHistory_,
+ ImageManager& imageManager_,
+ LineAtlas& lineAtlas_)
+ : context(context_),
+ backend(backend_),
+ state(updateParameters.transformState),
+ evaluatedLight(evaluatedLight_),
+ staticData(staticData_),
+ frameHistory(frameHistory_),
+ 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..4a2c2c6f12 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/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 FrameHistory;
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&,
+ FrameHistory&,
+ ImageManager&,
+ LineAtlas&);
+
+ gl::Context& context;
+ RendererBackend& backend;
+
+ const TransformState& state;
+ const EvaluatedLight& evaluatedLight;
+
+ RenderStaticData& staticData;
+ FrameHistory& frameHistory;
+ 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);
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp
index 36d2e98082..652948c8df 100644
--- a/src/mbgl/renderer/paint_property_binder.hpp
+++ b/src/mbgl/renderer/paint_property_binder.hpp
@@ -5,6 +5,7 @@
#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>
#include <bitset>
@@ -217,7 +218,11 @@ public:
}
float interpolationFactor(float currentZoom) const override {
- return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom);
+ if (function.useIntegerZoom) {
+ return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom));
+ } else {
+ return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom);
+ }
}
T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
deleted file mode 100644
index fbaf40d5c0..0000000000
--- a/src/mbgl/renderer/painter.cpp
+++ /dev/null
@@ -1,430 +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/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/style.hpp>
-#include <mbgl/style/layer_impl.hpp>
-
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/renderer/render_background_layer.hpp>
-#include <mbgl/renderer/render_custom_layer.hpp>
-#include <mbgl/style/layers/custom_layer_impl.hpp>
-#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
-
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
-
-#include <mbgl/programs/program_parameters.hpp>
-#include <mbgl/programs/programs.hpp>
-
-#include <mbgl/algorithm/generate_clip_ids.hpp>
-#include <mbgl/algorithm/generate_clip_ids_impl.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_FADE_DURATION);
-}
-
-void Painter::cleanup() {
- context.performCleanup();
-}
-
-void Painter::render(const Style& style, const FrameData& frame_, View& view, SpriteAtlas& annotationSpriteAtlas) {
- frame = frame_;
- if (frame.contextMode == GLContextMode::Shared) {
- context.setDirtyState();
- }
-
- PaintParameters parameters {
-#ifndef NDEBUG
- paintMode() == PaintMode::Overdraw ? *overdrawPrograms : *programs,
-#else
- *programs,
-#endif
- view
- };
-
- glyphAtlas = style.glyphAtlas.get();
- spriteAtlas = style.spriteAtlas.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_FADE_DURATION : Milliseconds(0));
-
-
- // - UPLOAD PASS -------------------------------------------------------------------------------
- // Uploads all required buffers and images before we do any actual rendering.
- {
- MBGL_DEBUG_GROUP(context, "upload");
-
- spriteAtlas->upload(context, 0);
-
- lineAtlas->upload(context, 0);
- glyphAtlas->upload(context, 0);
- frameHistory.upload(context, 0);
- annotationSpriteAtlas.upload(context, 0);
-
- for (const auto& item : order) {
- for (const auto& tileRef : item.tiles) {
- const auto& bucket = tileRef.get().tile.getBucket(item.layer);
- if (bucket && bucket->needsUpload()) {
- bucket->upload(context);
- }
- }
- }
- }
-
- // - 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.
- algorithm::ClipIDGenerator generator;
- for (const auto& source : sources) {
- source->startRender(generator, projMatrix, nearClippedProjMatrix, state);
- }
-
- MBGL_DEBUG_GROUP(context, "clipping masks");
-
- for (const auto& stencil : generator.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.bindVertexArray = 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<RenderCustomLayer>()) {
- MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - custom");
-
- // Reset GL state to a known state so the CustomLayer always has a clean slate.
- context.bindVertexArray = 0;
- context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadOnly));
- context.setStencilMode(gl::StencilMode::disabled());
- context.setColorMode(colorModeForRenderPass());
-
- layer.as<RenderCustomLayer>()->impl->render(state);
-
- // Reset the view back to our original one, just in case the CustomLayer changed
- // the viewport or Framebuffer.
- parameters.view.bind();
- context.setDirtyState();
- } 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, {});
-
- for (auto& tileRef : item.tiles) {
- auto& tile = tileRef.get();
-
- MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id));
- auto bucket = tile.tile.getBucket(layer);
- bucket->render(*this, parameters, layer, tile);
- }
-
- parameters.view.bind();
- context.bindTexture(extrusionTexture->getTexture());
-
- mat4 viewportMat;
- matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
-
- const PaintProperties<>::Evaluated 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(),
- layer.getID());
- } else {
- for (auto& tileRef : item.tiles) {
- auto& tile = tileRef.get();
- MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id));
- auto bucket = tile.tile.getBucket(layer);
- bucket->render(*this, parameters, layer, tile);
- }
- }
- }
-
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
- }
-}
-
-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 1919ad924e..0000000000
--- a/src/mbgl/renderer/painter.hpp
+++ /dev/null
@@ -1,186 +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/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/style/style.hpp>
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/offscreen_texture.hpp>
-
-#include <array>
-#include <vector>
-#include <set>
-#include <map>
-
-namespace mbgl {
-
-class RenderTile;
-class SpriteAtlas;
-class View;
-class GlyphAtlas;
-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;
-
-namespace style {
-class Style;
-class Source;
-} // namespace style
-
-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(const style::Style&,
- const FrameData&,
- View&,
- SpriteAtlas& annotationSpriteAtlas);
-
- void cleanup();
-
- void renderClippingMask(const UnwrappedTileID&, const ClipID&);
- void renderTileDebug(const RenderTile&);
- 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 RenderTile&);
- void renderBackground(PaintParameters&, const RenderBackgroundLayer&);
-
-#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;
-
-private:
- std::vector<RenderItem> determineRenderOrder(const style::Style&);
-
- 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
-
-private:
- gl::Context& context;
-
- 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);
-
- SpriteAtlas* spriteAtlas = nullptr;
- GlyphAtlas* glyphAtlas = 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;
-
- SegmentVector<FillAttributes> tileTriangleSegments;
- SegmentVector<DebugAttributes> tileBorderSegments;
- SegmentVector<RasterAttributes> rasterSegments;
- SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp
deleted file mode 100644
index 9bd9431082..0000000000
--- a/src/mbgl/renderer/painter_background.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/render_background_layer.hpp>
-#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/sprite/sprite_atlas.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::Evaluated& background = layer.evaluated;
-
- style::FillPaintProperties::Evaluated 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<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(background.get<BackgroundPattern>().from);
- optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(background.get<BackgroundPattern>().to);
-
- if (!imagePosA || !imagePosB)
- return;
-
- spriteAtlas->bind(true, 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,
- *imagePosA,
- *imagePosB,
- background.get<BackgroundPattern>(),
- tileID,
- state
- ),
- tileVertexBuffer,
- quadTriangleIndexBuffer,
- tileTriangleSegments,
- paintAttibuteData,
- properties,
- state.getZoom(),
- layer.getID()
- );
- }
- } 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(),
- layer.getID()
- );
- }
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_circle.cpp b/src/mbgl/renderer/painter_circle.cpp
deleted file mode 100644
index ecd7598de9..0000000000
--- a/src/mbgl/renderer/painter_circle.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/circle_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/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::Evaluated& 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(),
- layer.getID()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp
deleted file mode 100644
index 162f3b1d96..0000000000
--- a/src/mbgl/renderer/painter_clipping.cpp
+++ /dev/null
@@ -1,38 +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::Evaluated 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(),
- "clipping"
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp
deleted file mode 100644
index 9a24ab5422..0000000000
--- a/src/mbgl/renderer/painter_debug.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/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::PaintProperties<>::Evaluated 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(),
- "debug"
- );
- };
-
- 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 });
- }
-}
-
-#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/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp
deleted file mode 100644
index 7264735692..0000000000
--- a/src/mbgl/renderer/painter_fill.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/fill_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/render_fill_layer.hpp>
-#include <mbgl/style/layers/fill_layer_impl.hpp>
-#include <mbgl/sprite/sprite_atlas.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::Evaluated& properties = layer.evaluated;
-
- if (!properties.get<FillPattern>().from.empty()) {
- if (pass != RenderPass::Translucent) {
- return;
- }
-
- optional<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(properties.get<FillPattern>().from);
- optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(properties.get<FillPattern>().to);
-
- if (!imagePosA || !imagePosB) {
- return;
- }
-
- spriteAtlas->bind(true, 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,
- *imagePosA,
- *imagePosB,
- properties.get<FillPattern>(),
- tile.id,
- state
- ),
- *bucket.vertexBuffer,
- indexBuffer,
- segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom(),
- layer.getID()
- );
- };
-
- 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, pass == RenderPass::Opaque
- ? gl::DepthMode::ReadWrite
- : gl::DepthMode::ReadOnly),
- 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(),
- layer.getID()
- );
- };
-
- 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/painter_fill_extrusion.cpp b/src/mbgl/renderer/painter_fill_extrusion.cpp
deleted file mode 100644
index 5581cfe983..0000000000
--- a/src/mbgl/renderer/painter_fill_extrusion.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/fill_extrusion_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
-#include <mbgl/sprite/sprite_atlas.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::Evaluated& properties = layer.evaluated;
-
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- if (!properties.get<FillExtrusionPattern>().from.empty()) {
- optional<SpriteAtlasElement> imagePosA =
- spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().from);
- optional<SpriteAtlasElement> imagePosB =
- spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().to);
-
- if (!imagePosA || !imagePosB) {
- return;
- }
-
- spriteAtlas->bind(true, 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),
- *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(),
- layer.getID());
-
- } 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(),
- layer.getID());
- };
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp
deleted file mode 100644
index 209b1447d0..0000000000
--- a/src/mbgl/renderer/painter_line.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/line_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/render_line_layer.hpp>
-#include <mbgl/style/layers/line_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/line_program.hpp>
-#include <mbgl/sprite/sprite_atlas.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 LinePaintProperties::Evaluated& 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(),
- layer.getID()
- );
- };
-
- 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,
- layer.dashLineWidth,
- lineAtlas->getSize().width));
-
- } else if (!properties.get<LinePattern>().from.empty()) {
- optional<SpriteAtlasElement> posA = spriteAtlas->getPattern(properties.get<LinePattern>().from);
- optional<SpriteAtlasElement> posB = spriteAtlas->getPattern(properties.get<LinePattern>().to);
-
- if (!posA || !posB)
- return;
-
- spriteAtlas->bind(true, context, 0);
-
- draw(parameters.programs.linePattern,
- LinePatternProgram::uniformValues(
- properties,
- tile,
- state,
- pixelsToGLUnits,
- *posA,
- *posB));
-
- } else {
- draw(parameters.programs.line,
- LineProgram::uniformValues(
- properties,
- tile,
- state,
- pixelsToGLUnits));
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp
deleted file mode 100644
index f0e5399f4a..0000000000
--- a/src/mbgl/renderer/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/raster_bucket.hpp>
-#include <mbgl/renderer/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 RenderTile& tile) {
- if (pass != RenderPass::Translucent)
- return;
- if (!bucket.hasData())
- return;
-
- const RasterPaintProperties::Evaluated& 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{ tile.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 }} },
- },
- rasterVertexBuffer,
- quadTriangleIndexBuffer,
- rasterSegments,
- paintAttributeData,
- properties,
- state.getZoom(),
- layer.getID()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp
deleted file mode 100644
index 6462eebdcc..0000000000
--- a/src/mbgl/renderer/painter_symbol.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
-#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
-#include <mbgl/sprite/sprite_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/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(),
- layer.getID()
- );
- };
-
- if (bucket.hasIconData()) {
- auto values = layer.iconPropertyValues(layout);
- auto paintPropertyValues = layer.iconPaintProperties();
-
- SpriteAtlas& atlas = *bucket.spriteAtlas;
- const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 ||
- frame.pixelRatio != atlas.getPixelRatio() ||
- bucket.iconsNeedLinear;
- const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0;
- atlas.bind(bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed, context, 0);
-
- const Size texsize = atlas.getSize();
-
- 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()) {
- glyphAtlas->bind(context, 0);
-
- auto values = layer.textPropertyValues(layout);
- auto paintPropertyValues = layer.textPaintProperties();
-
- const Size texsize = glyphAtlas->getSize();
-
- 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::PaintProperties<>::Evaluated 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(),
- layer.getID()
- );
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/possibly_evaluated_property_value.hpp b/src/mbgl/renderer/possibly_evaluated_property_value.hpp
index a0bcec2bf1..e662d5dfb1 100644
--- a/src/mbgl/renderer/possibly_evaluated_property_value.hpp
+++ b/src/mbgl/renderer/possibly_evaluated_property_value.hpp
@@ -19,7 +19,9 @@ private:
public:
PossiblyEvaluatedPropertyValue() = default;
- PossiblyEvaluatedPropertyValue(Value v) : value(std::move(v)) {}
+ PossiblyEvaluatedPropertyValue(Value v, bool useIntegerZoom_ = false)
+ : value(std::move(v)),
+ useIntegerZoom(useIntegerZoom_) {}
bool isConstant() const {
return value.template is<T>();
@@ -43,15 +45,21 @@ 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);
},
[&] (const style::CompositeFunction<T>& function) {
- return function.evaluate(zoom, feature, defaultValue);
+ if (useIntegerZoom) {
+ return function.evaluate(floor(zoom), feature, defaultValue);
+ } else {
+ return function.evaluate(zoom, feature, defaultValue);
+ }
}
);
}
+
+ bool useIntegerZoom;
};
namespace util {
diff --git a/src/mbgl/renderer/property_evaluation_parameters.hpp b/src/mbgl/renderer/property_evaluation_parameters.hpp
index 39b663bdb9..da6a4a0892 100644
--- a/src/mbgl/renderer/property_evaluation_parameters.hpp
+++ b/src/mbgl/renderer/property_evaluation_parameters.hpp
@@ -11,20 +11,24 @@ public:
: z(z_),
now(Clock::time_point::max()),
zoomHistory(),
- defaultFadeDuration(0) {}
+ defaultFadeDuration(0),
+ useIntegerZoom(false) {}
PropertyEvaluationParameters(ZoomHistory zoomHistory_,
TimePoint now_,
- Duration defaultFadeDuration_)
+ Duration defaultFadeDuration_,
+ bool useIntegerZoom_ = false)
: z(zoomHistory_.lastZoom),
now(std::move(now_)),
zoomHistory(std::move(zoomHistory_)),
- defaultFadeDuration(std::move(defaultFadeDuration_)) {}
+ defaultFadeDuration(std::move(defaultFadeDuration_)),
+ useIntegerZoom(useIntegerZoom_) {}
float z;
TimePoint now;
ZoomHistory zoomHistory;
Duration defaultFadeDuration;
+ bool useIntegerZoom;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp
deleted file mode 100644
index ee8ef24071..0000000000
--- a/src/mbgl/renderer/raster_bucket.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <mbgl/renderer/raster_bucket.hpp>
-#include <mbgl/renderer/render_raster_layer.hpp>
-#include <mbgl/programs/raster_program.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/gl/context.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-RasterBucket::RasterBucket(UnassociatedImage&& image_) : image(std::move(image_)) {
-}
-
-void RasterBucket::upload(gl::Context& context) {
- texture = context.createTexture(std::move(image));
- uploaded = true;
-}
-
-void RasterBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), tile);
-}
-
-bool RasterBucket::hasData() const {
- return true;
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp
deleted file mode 100644
index 334954e3f4..0000000000
--- a/src/mbgl/renderer/raster_bucket.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/gl/texture.hpp>
-
-namespace mbgl {
-
-class RasterBucket : public Bucket {
-public:
- RasterBucket(UnassociatedImage&&);
-
- void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
- bool hasData() const override;
-
- UnassociatedImage image;
- optional<gl::Texture> texture;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_background_layer.cpp b/src/mbgl/renderer/render_background_layer.cpp
deleted file mode 100644
index 485d4b16c6..0000000000
--- a/src/mbgl/renderer/render_background_layer.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <mbgl/renderer/render_background_layer.hpp>
-#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/renderer/bucket.hpp>
-
-namespace mbgl {
-
-RenderBackgroundLayer::RenderBackgroundLayer(const style::BackgroundLayer::Impl& _impl)
- : RenderLayer(style::LayerType::Background, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderBackgroundLayer::clone() const {
- return std::make_unique<RenderBackgroundLayer>(*this);
-}
-
-std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &,
- const std::vector<const RenderLayer *> &) const {
- assert(false);
- return nullptr;
-}
-
-void RenderBackgroundLayer::cascade(const CascadeParameters &parameters) {
- unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
-}
-
-void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters &parameters) {
- evaluated = unevaluated.evaluate(parameters);
-
- passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent
- : RenderPass::None;
-}
-
-bool RenderBackgroundLayer::hasTransition() const {
- return unevaluated.hasTransition();
-}
-
-}
diff --git a/src/mbgl/renderer/render_circle_layer.cpp b/src/mbgl/renderer/render_circle_layer.cpp
deleted file mode 100644
index f59c174dd3..0000000000
--- a/src/mbgl/renderer/render_circle_layer.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#include <mbgl/renderer/render_circle_layer.hpp>
-#include <mbgl/renderer/circle_bucket.hpp>
-#include <mbgl/style/layers/circle_layer_impl.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/intersection_tests.hpp>
-
-namespace mbgl {
-
-RenderCircleLayer::RenderCircleLayer(const style::CircleLayer::Impl& _impl)
- : RenderLayer(style::LayerType::Circle, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderCircleLayer::clone() const {
- return std::make_unique<RenderCircleLayer>(*this);
-}
-
-std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
- return std::make_unique<CircleBucket>(parameters, layers);
-}
-
-void RenderCircleLayer::cascade(const CascadeParameters& parameters) {
- unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
-}
-
-void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) {
- evaluated = unevaluated.evaluate(parameters);
-
- passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 ||
- evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0)
- && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 ||
- evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0)
- && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 ||
- evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0))
- ? RenderPass::Translucent : RenderPass::None;
-}
-
-bool RenderCircleLayer::hasTransition() const {
- return unevaluated.hasTransition();
-}
-
-bool RenderCircleLayer::queryIntersectsFeature(
- const GeometryCoordinates& queryGeometry,
- const GeometryTileFeature& feature,
- const float zoom,
- const float bearing,
- const float pixelsToTileUnits) const {
-
- // Translate query geometry
- auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
- queryGeometry,
- evaluated.get<style::CircleTranslate>(),
- evaluated.get<style::CircleTranslateAnchor>(),
- bearing,
- pixelsToTileUnits);
-
- // Evaluate function
- auto circleRadius = evaluated.get<style::CircleRadius>()
- .evaluate(feature, zoom, style::CircleRadius::defaultValue())
- * pixelsToTileUnits;
-
- // Test intersection
- return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius);
-}
-
-}
diff --git a/src/mbgl/renderer/render_custom_layer.cpp b/src/mbgl/renderer/render_custom_layer.cpp
deleted file mode 100644
index 66dd57b3d3..0000000000
--- a/src/mbgl/renderer/render_custom_layer.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <mbgl/renderer/render_custom_layer.hpp>
-#include <mbgl/style/layers/custom_layer_impl.hpp>
-#include <mbgl/renderer/bucket.hpp>
-
-namespace mbgl {
-
-RenderCustomLayer::RenderCustomLayer(const style::CustomLayer::Impl& _impl)
- : RenderLayer(style::LayerType::Custom, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderCustomLayer::clone() const {
- return std::make_unique<RenderCustomLayer>(*this);
-}
-
-void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) {
- passes = RenderPass::Translucent;
-}
-
-bool RenderCustomLayer::hasTransition() const {
- return false;
-}
-
-std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
- assert(false);
- return nullptr;
-}
-
-}
diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/render_fill_extrusion_layer.cpp
deleted file mode 100644
index f6ba164d8c..0000000000
--- a/src/mbgl/renderer/render_fill_extrusion_layer.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
-#include <mbgl/renderer/fill_extrusion_bucket.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/intersection_tests.hpp>
-
-namespace mbgl {
-
-RenderFillExtrusionLayer::RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl& _impl)
- : RenderLayer(style::LayerType::FillExtrusion, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderFillExtrusionLayer::clone() const {
- return std::make_unique<RenderFillExtrusionLayer>(*this);
-}
-
-std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
- return std::make_unique<FillExtrusionBucket>(parameters, layers);
-}
-
-void RenderFillExtrusionLayer::cascade(const CascadeParameters& parameters) {
- unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
-}
-
-void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
- evaluated = unevaluated.evaluate(parameters);
-
- passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent
- : RenderPass::None;
-}
-
-bool RenderFillExtrusionLayer::hasTransition() const {
- return unevaluated.hasTransition();
-}
-
-bool RenderFillExtrusionLayer::queryIntersectsFeature(
- const GeometryCoordinates& queryGeometry,
- const GeometryTileFeature& feature,
- const float,
- const float bearing,
- const float pixelsToTileUnits) const {
-
- auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
- queryGeometry,
- evaluated.get<style::FillExtrusionTranslate>(),
- evaluated.get<style::FillExtrusionTranslateAnchor>(),
- bearing,
- pixelsToTileUnits);
-
- return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_fill_layer.cpp b/src/mbgl/renderer/render_fill_layer.cpp
deleted file mode 100644
index 1af139cded..0000000000
--- a/src/mbgl/renderer/render_fill_layer.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <mbgl/renderer/render_fill_layer.hpp>
-#include <mbgl/renderer/fill_bucket.hpp>
-#include <mbgl/style/layers/fill_layer_impl.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/intersection_tests.hpp>
-
-namespace mbgl {
-
-RenderFillLayer::RenderFillLayer(const style::FillLayer::Impl& _impl)
- : RenderLayer(style::LayerType::Fill, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderFillLayer::clone() const {
- return std::make_unique<RenderFillLayer>(*this);
-}
-
-std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
- return std::make_unique<FillBucket>(parameters, layers);
-}
-
-void RenderFillLayer::cascade(const CascadeParameters& parameters) {
- unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
-}
-
-void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) {
- evaluated = unevaluated.evaluate(parameters);
-
- if (unevaluated.get<style::FillOutlineColor>().isUndefined()) {
- evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>();
- }
-
- passes = RenderPass::None;
-
- if (evaluated.get<style::FillAntialias>()) {
- passes |= RenderPass::Translucent;
- }
-
- if (!unevaluated.get<style::FillPattern>().isUndefined()
- || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f
- || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) {
- passes |= RenderPass::Translucent;
- } else {
- passes |= RenderPass::Opaque;
- }
-}
-
-bool RenderFillLayer::hasTransition() const {
- return unevaluated.hasTransition();
-}
-
-bool RenderFillLayer::queryIntersectsFeature(
- const GeometryCoordinates& queryGeometry,
- const GeometryTileFeature& feature,
- const float,
- const float bearing,
- const float pixelsToTileUnits) const {
-
- auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
- queryGeometry,
- evaluated.get<style::FillTranslate>(),
- evaluated.get<style::FillTranslateAnchor>(),
- bearing,
- pixelsToTileUnits);
-
- return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
-}
-
-
-}
diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp
deleted file mode 100644
index 787211c30a..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(const RenderLayer& layer_,
- std::vector<std::reference_wrapper<RenderTile>> tiles_ = {})
- : layer(layer_), tiles(std::move(tiles_)) {
- }
-
- const RenderLayer& layer;
- std::vector<std::reference_wrapper<RenderTile>> tiles;
-};
-
-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 6699f39144..eb2b74ffe0 100644
--- a/src/mbgl/renderer/render_layer.cpp
+++ b/src/mbgl/renderer/render_layer.cpp
@@ -1,14 +1,55 @@
#include <mbgl/renderer/render_layer.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/style/types.hpp>
+#include <mbgl/renderer/render_tile.hpp>
namespace mbgl {
-RenderLayer::RenderLayer(style::LayerType type_, const style::Layer::Impl& baseImpl_)
- : type(type_), baseImpl(baseImpl_) {
+using namespace style;
+
+std::unique_ptr<RenderLayer> RenderLayer::create(Immutable<Layer::Impl> impl) {
+ switch (impl->type) {
+ case LayerType::Fill:
+ return std::make_unique<RenderFillLayer>(staticImmutableCast<FillLayer::Impl>(impl));
+ case LayerType::Line:
+ return std::make_unique<RenderLineLayer>(staticImmutableCast<LineLayer::Impl>(impl));
+ case LayerType::Circle:
+ return std::make_unique<RenderCircleLayer>(staticImmutableCast<CircleLayer::Impl>(impl));
+ case LayerType::Symbol:
+ return std::make_unique<RenderSymbolLayer>(staticImmutableCast<SymbolLayer::Impl>(impl));
+ case LayerType::Raster:
+ return std::make_unique<RenderRasterLayer>(staticImmutableCast<RasterLayer::Impl>(impl));
+ case LayerType::Background:
+ return std::make_unique<RenderBackgroundLayer>(staticImmutableCast<BackgroundLayer::Impl>(impl));
+ case LayerType::Custom:
+ return std::make_unique<RenderCustomLayer>(staticImmutableCast<CustomLayer::Impl>(impl));
+ case LayerType::FillExtrusion:
+ return std::make_unique<RenderFillExtrusionLayer>(staticImmutableCast<FillExtrusionLayer::Impl>(impl));
+ }
+
+ // Not reachable, but placate GCC.
+ assert(false);
+ return nullptr;
+}
+
+RenderLayer::RenderLayer(style::LayerType type_, Immutable<style::Layer::Impl> baseImpl_)
+ : type(type_),
+ baseImpl(baseImpl_) {
+}
+
+void RenderLayer::setImpl(Immutable<style::Layer::Impl> impl) {
+ baseImpl = impl;
}
const std::string& RenderLayer::getID() const {
- return baseImpl.id;
+ return baseImpl->id;
}
bool RenderLayer::hasRenderPass(RenderPass pass) const {
@@ -17,9 +58,14 @@ bool RenderLayer::hasRenderPass(RenderPass pass) const {
bool RenderLayer::needsRendering(float zoom) const {
return passes != RenderPass::None
- && baseImpl.visibility != style::VisibilityType::None
- && baseImpl.minZoom <= zoom
- && baseImpl.maxZoom >= zoom;
+ && baseImpl->visibility != style::VisibilityType::None
+ && baseImpl->minZoom <= zoom
+ && baseImpl->maxZoom >= zoom;
+}
+
+void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>> tiles) {
+ renderTiles = std::move(tiles);
}
-} \ No newline at end of file
+} //namespace mbgl
+
diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp
index eea2ec1f61..dfc6bcf2fd 100644
--- a/src/mbgl/renderer/render_layer.hpp
+++ b/src/mbgl/renderer/render_layer.hpp
@@ -12,27 +12,27 @@ namespace mbgl {
class Bucket;
class BucketParameters;
-class CascadeParameters;
+class TransitionParameters;
class PropertyEvaluationParameters;
+class PaintParameters;
+class RenderSource;
+class RenderTile;
class RenderLayer {
-
protected:
- RenderLayer(style::LayerType, const style::Layer::Impl&);
+ RenderLayer(style::LayerType, Immutable<style::Layer::Impl>);
const style::LayerType type;
public:
+ static std::unique_ptr<RenderLayer> create(Immutable<style::Layer::Impl>);
virtual ~RenderLayer() = default;
- // Create an identical copy of this layer.
- virtual std::unique_ptr<RenderLayer> clone() const = 0;
-
- // Partially evaluate paint properties based on a set of classes.
- virtual void cascade(const CascadeParameters&) = 0;
+ // Begin transitions for any properties that have changed since the last frame.
+ virtual void transition(const TransitionParameters&) = 0;
- // Fully evaluate cascaded paint properties based on a zoom level.
+ // Fully evaluate possibly-transitioning paint properties based on a zoom level.
virtual void evaluate(const PropertyEvaluationParameters&) = 0;
// Returns true if any paint properties have active transitions.
@@ -61,6 +61,8 @@ public:
// Checks whether this layer can be rendered.
bool needsRendering(float zoom) const;
+ virtual void render(PaintParameters&, RenderSource*) = 0;
+
// Check wether the given geometry intersects
// with the feature
virtual bool queryIntersectsFeature(
@@ -72,15 +74,21 @@ public:
virtual std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const = 0;
+ void setRenderTiles(std::vector<std::reference_wrapper<RenderTile>>);
// Private implementation
- const style::Layer::Impl& baseImpl;
+ Immutable<style::Layer::Impl> baseImpl;
+ void setImpl(Immutable<style::Layer::Impl>);
friend std::string layoutKey(const RenderLayer&);
-protected:
+protected:
// 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_light.cpp b/src/mbgl/renderer/render_light.cpp
index 134e1829e0..85768cff47 100644
--- a/src/mbgl/renderer/render_light.cpp
+++ b/src/mbgl/renderer/render_light.cpp
@@ -2,25 +2,17 @@
namespace mbgl {
-RenderLight::RenderLight(std::shared_ptr<const style::Light::Impl> impl_)
- : impl(std::move(impl_)) {
+RenderLight::RenderLight(Immutable<style::Light::Impl> impl_)
+ : impl(std::move(impl_)),
+ transitioning(impl->properties.untransitioned()) {
}
-RenderLight::RenderLight(std::shared_ptr<const style::Light::Impl> impl_, const TransitioningLight transitioning_)
- : impl(std::move(impl_))
- , transitioning(transitioning_) {
-}
-
-std::unique_ptr<RenderLight> RenderLight::copy(std::shared_ptr<const style::Light::Impl> impl_) const {
- return std::make_unique<RenderLight>(std::move(impl_), transitioning);
-}
-
-void RenderLight::transition(const CascadeParameters& parameters) {
- transitioning = TransitioningLight(impl->properties, std::move(transitioning), parameters);
+void RenderLight::transition(const TransitionParameters& parameters) {
+ transitioning = impl->properties.transitioned(parameters, std::move(transitioning));
}
void RenderLight::evaluate(const PropertyEvaluationParameters& parameters) {
- evaluated = EvaluatedLight(transitioning, parameters);
+ evaluated = transitioning.evaluate(parameters);
}
bool RenderLight::hasTransition() const {
diff --git a/src/mbgl/renderer/render_light.hpp b/src/mbgl/renderer/render_light.hpp
index 275f3ae8ba..f13f925318 100644
--- a/src/mbgl/renderer/render_light.hpp
+++ b/src/mbgl/renderer/render_light.hpp
@@ -1,98 +1,29 @@
#pragma once
#include <mbgl/style/light_impl.hpp>
-#include <mbgl/style/light_properties.hpp>
-#include <mbgl/renderer/transitioning_property.hpp>
-#include <mbgl/renderer/cascade_parameters.hpp>
-#include <mbgl/renderer/property_evaluator.hpp>
-#include <mbgl/renderer/property_evaluation_parameters.hpp>
-#include <mbgl/util/ignore.hpp>
-
-#include <memory>
+#include <mbgl/util/immutable.hpp>
namespace mbgl {
-template <class TypeList>
-class Transitioning;
-
-template <class... Ps>
-class Transitioning<TypeList<Ps...>> : public IndexedTuple<
- TypeList<Ps...>,
- TypeList<TransitioningProperty<typename Ps::ValueType>...>>
-{
-private:
- using Properties = TypeList<Ps...>;
- using Raw = IndexedTuple<Properties, Properties>;
- using Super = IndexedTuple<
- TypeList<Ps...>,
- TypeList<TransitioningProperty<typename Ps::ValueType>...>>;
-
-public:
- Transitioning() = default;
- Transitioning(const Raw& raw, Transitioning&& prior, const CascadeParameters& params)
- : Super {
- TransitioningProperty<typename Ps::ValueType>(
- raw.template get<Ps>().value,
- std::move(prior.template get<Ps>()),
- raw.template get<Ps>().transition.reverseMerge(params.transition),
- params.now)...
- } {}
-
- bool hasTransition() const {
- bool result = false;
- util::ignore({ result |= this->template get<Ps>().hasTransition()... });
- return result;
- }
-};
-
-template <class TypeList>
-class Evaluated;
+class TransitionParameters;
+class PropertyEvaluationParameters;
-template <class... Ps>
-class Evaluated<TypeList<Ps...>> : public IndexedTuple<
- TypeList<Ps...>,
- TypeList<typename Ps::Type...>>
-{
-private:
- using Properties = TypeList<Ps...>;
- using TransitioningPs = Transitioning<Properties>;
- using Super = IndexedTuple<
- TypeList<Ps...>,
- TypeList<typename Ps::Type...>>;
-
-public:
- Evaluated() = default;
- Evaluated(TransitioningPs& transitioning, const PropertyEvaluationParameters& params)
- : Super {
- transitioning.template get<Ps>()
- .evaluate(PropertyEvaluator<typename Ps::Type>(params, Ps::defaultValue()), params.now)...
- } {}
-};
-
-using TransitioningLight = Transitioning<style::LightProperties>;
-using EvaluatedLight = Evaluated<style::LightProperties>;
+using TransitioningLight = style::LightProperties::Unevaluated;
+using EvaluatedLight = style::LightProperties::PossiblyEvaluated;
class RenderLight {
public:
- RenderLight(std::shared_ptr<const style::Light::Impl>);
-
- // Creates a copy intitalized with previous transitioning light
- RenderLight(std::shared_ptr<const style::Light::Impl>, const TransitioningLight);
+ RenderLight(Immutable<style::Light::Impl>);
- // creates a copy initialized with previous transitioning
- // values
- std::unique_ptr<RenderLight> copy(std::shared_ptr<const style::Light::Impl>) const;
-
- void transition(const CascadeParameters&);
+ void transition(const TransitionParameters&);
void evaluate(const PropertyEvaluationParameters&);
bool hasTransition() const;
const EvaluatedLight& getEvaluated() const;
- const std::shared_ptr<const style::Light::Impl> impl;
+ Immutable<style::Light::Impl> impl;
private:
-
TransitioningLight transitioning;
EvaluatedLight evaluated;
};
diff --git a/src/mbgl/renderer/render_line_layer.cpp b/src/mbgl/renderer/render_line_layer.cpp
deleted file mode 100644
index 06c2564516..0000000000
--- a/src/mbgl/renderer/render_line_layer.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-#include <mbgl/renderer/render_line_layer.hpp>
-#include <mbgl/renderer/line_bucket.hpp>
-#include <mbgl/style/layers/line_layer_impl.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/intersection_tests.hpp>
-
-namespace mbgl {
-
-RenderLineLayer::RenderLineLayer(const style::LineLayer::Impl& _impl)
- : RenderLayer(style::LayerType::Line, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderLineLayer::clone() const {
- return std::make_unique<RenderLineLayer>(*this);
-}
-
-std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
- return std::make_unique<LineBucket>(parameters, layers, impl->layout);
-}
-
-void RenderLineLayer::cascade(const CascadeParameters& parameters) {
- unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
-}
-
-void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) {
- // for scaling dasharrays
- auto dashArrayParams = parameters;
- dashArrayParams.z = std::floor(dashArrayParams.z);
- dashLineWidth = unevaluated.evaluate<style::LineWidth>(dashArrayParams);
-
- evaluated = unevaluated.evaluate(parameters);
-
- passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0
- && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0
- && evaluated.get<style::LineWidth>() > 0)
- ? RenderPass::Translucent : RenderPass::None;
-}
-
-bool RenderLineLayer::hasTransition() const {
- return unevaluated.hasTransition();
-}
-
-optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) {
- if (offset == 0) return {};
-
- GeometryCollection newRings;
- Point<double> zero(0, 0);
- for (const auto& ring : rings) {
- newRings.emplace_back();
- auto& newRing = newRings.back();
-
- for (auto i = ring.begin(); i != ring.end(); i++) {
- auto& p = *i;
-
- Point<double> aToB = i == ring.begin() ?
- zero :
- util::perp(util::unit(convertPoint<double>(p - *(i - 1))));
- Point<double> bToC = i + 1 == ring.end() ?
- zero :
- util::perp(util::unit(convertPoint<double>(*(i + 1) - p)));
- Point<double> extrude = util::unit(aToB + bToC);
-
- const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y;
- extrude *= (1.0 / cosHalfAngle);
-
- newRing.push_back(convertPoint<int16_t>(extrude * offset) + p);
- }
- }
-
- return newRings;
-}
-
-bool RenderLineLayer::queryIntersectsFeature(
- const GeometryCoordinates& queryGeometry,
- const GeometryTileFeature& feature,
- const float zoom,
- const float bearing,
- const float pixelsToTileUnits) const {
-
- // Translate query geometry
- auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
- queryGeometry,
- evaluated.get<style::LineTranslate>(),
- evaluated.get<style::LineTranslateAnchor>(),
- bearing,
- pixelsToTileUnits);
-
- // Evaluate function
- auto offset = evaluated.get<style::LineOffset>()
- .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits;
-
- // Apply offset to geometry
- auto offsetGeometry = offsetLine(feature.getGeometries(), offset);
-
- // Test intersection
- const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits;
- return util::polygonIntersectsBufferedMultiLine(
- translatedQueryGeometry.value_or(queryGeometry),
- offsetGeometry.value_or(feature.getGeometries()),
- halfWidth);
-}
-
-float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const {
- float lineWidth = evaluated.get<style::LineWidth>();
- float gapWidth = evaluated.get<style::LineGapWidth>()
- .evaluate(feature, zoom, style::LineGapWidth::defaultValue());
- if (gapWidth) {
- return gapWidth + 2 * lineWidth;
- } else {
- return lineWidth;
- }
-}
-
-
-} // 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_raster_layer.cpp b/src/mbgl/renderer/render_raster_layer.cpp
deleted file mode 100644
index 5e664e6f58..0000000000
--- a/src/mbgl/renderer/render_raster_layer.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include <mbgl/renderer/render_raster_layer.hpp>
-#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/style/layers/raster_layer_impl.hpp>
-
-namespace mbgl {
-
-RenderRasterLayer::RenderRasterLayer(const style::RasterLayer::Impl& _impl)
- : RenderLayer(style::LayerType::Raster, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderRasterLayer::clone() const {
- return std::make_unique<RenderRasterLayer>(*this);
-}
-
-std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
- assert(false);
- return nullptr;
-}
-
-void RenderRasterLayer::cascade(const CascadeParameters& parameters) {
- unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
-}
-
-void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) {
- evaluated = unevaluated.evaluate(parameters);
-
- passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None;
-}
-
-bool RenderRasterLayer::hasTransition() const {
- return unevaluated.hasTransition();
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp
index 643d92fe81..7723a1c7ca 100644
--- a/src/mbgl/renderer/render_source.cpp
+++ b/src/mbgl/renderer/render_source.cpp
@@ -1,12 +1,42 @@
#include <mbgl/renderer/render_source.hpp>
#include <mbgl/renderer/render_source_observer.hpp>
+#include <mbgl/renderer/sources/render_geojson_source.hpp>
+#include <mbgl/renderer/sources/render_raster_source.hpp>
+#include <mbgl/renderer/sources/render_vector_source.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/annotation/render_annotation_source.hpp>
+#include <mbgl/renderer/sources/render_image_source.hpp>
#include <mbgl/tile/tile.hpp>
namespace mbgl {
+using namespace style;
+
+std::unique_ptr<RenderSource> RenderSource::create(Immutable<Source::Impl> impl) {
+ switch (impl->type) {
+ case SourceType::Vector:
+ return std::make_unique<RenderVectorSource>(staticImmutableCast<VectorSource::Impl>(impl));
+ case SourceType::Raster:
+ return std::make_unique<RenderRasterSource>(staticImmutableCast<RasterSource::Impl>(impl));
+ case SourceType::GeoJSON:
+ return std::make_unique<RenderGeoJSONSource>(staticImmutableCast<GeoJSONSource::Impl>(impl));
+ case SourceType::Video:
+ assert(false);
+ return nullptr;
+ case SourceType::Annotations:
+ return std::make_unique<RenderAnnotationSource>(staticImmutableCast<AnnotationSource::Impl>(impl));
+ case SourceType::Image:
+ return std::make_unique<RenderImageSource>(staticImmutableCast<ImageSource::Impl>(impl));
+ }
+
+ // Not reachable, but placate GCC.
+ assert(false);
+ return nullptr;
+}
+
static RenderSourceObserver nullObserver;
-RenderSource::RenderSource(const style::Source::Impl& impl)
+RenderSource::RenderSource(Immutable<style::Source::Impl> impl)
: baseImpl(impl),
observer(&nullObserver) {
}
@@ -23,4 +53,8 @@ void RenderSource::onTileError(Tile& tile, std::exception_ptr error) {
observer->onTileError(*this, tile.id, error);
}
+bool RenderSource::isEnabled() const {
+ return enabled;
}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp
index d31347579e..8293923ff6 100644
--- a/src/mbgl/renderer/render_source.hpp
+++ b/src/mbgl/renderer/render_source.hpp
@@ -6,6 +6,7 @@
#include <mbgl/util/geo.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/layer_impl.hpp>
#include <unordered_map>
#include <vector>
@@ -14,69 +15,73 @@
namespace mbgl {
-class Painter;
+class PaintParameters;
class TransformState;
class RenderTile;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
class Tile;
class RenderSourceObserver;
class TileParameters;
-namespace algorithm {
-class ClipIDGenerator;
-} // namespace algorithm
-
class RenderSource : protected TileObserver {
public:
- RenderSource(const style::Source::Impl&);
- virtual ~RenderSource() = default;
+ static std::unique_ptr<RenderSource> create(Immutable<style::Source::Impl>);
- virtual bool isLoaded() const = 0;
+ // Check whether this source is of the given subtype.
+ template <class T>
+ bool is() const;
- // Called when the camera has changed. May load new tiles, unload obsolete tiles, or
- // trigger re-placement of existing complete tiles.
- virtual void updateTiles(const TileParameters&) = 0;
+ // Dynamically cast this source to the given subtype.
+ template <class T>
+ T* as() {
+ return is<T>() ? reinterpret_cast<T*>(this) : nullptr;
+ }
- // Removes all tiles (by putting them into the cache).
- virtual void removeTiles() = 0;
+ template <class T>
+ const T* as() const {
+ return is<T>() ? reinterpret_cast<const T*>(this) : nullptr;
+ }
- // Remove all tiles and clear the cache.
- virtual void invalidateTiles() = 0;
+ bool isEnabled() const;
+ virtual bool isLoaded() const = 0;
- // Request that all loaded tiles re-run the layout operation on the existing source
- // data with fresh style information.
- virtual void reloadTiles() = 0;
+ virtual void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) = 0;
- virtual void startRender(algorithm::ClipIDGenerator&,
- const mat4& projMatrix,
- const mat4& clipMatrix,
- const TransformState&) = 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 std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) 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;
void setObserver(RenderSourceObserver*);
- const style::Source::Impl& baseImpl;
- bool enabled = false;
+ Immutable<style::Source::Impl> baseImpl;
protected:
+ RenderSource(Immutable<style::Source::Impl>);
RenderSourceObserver* observer;
+ bool enabled = false;
+
void onTileChanged(Tile&) final;
void onTileError(Tile&, std::exception_ptr) final;
};
diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp
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_symbol_layer.cpp b/src/mbgl/renderer/render_symbol_layer.cpp
deleted file mode 100644
index 30d769e032..0000000000
--- a/src/mbgl/renderer/render_symbol_layer.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <mbgl/renderer/render_symbol_layer.hpp>
-#include <mbgl/layout/symbol_layout.hpp>
-#include <mbgl/renderer/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/tile/geometry_tile_data.hpp>
-
-namespace mbgl {
-
-RenderSymbolLayer::RenderSymbolLayer(const style::SymbolLayer::Impl& _impl)
- : RenderLayer(style::LayerType::Symbol, _impl),
- impl(&_impl) {
-}
-
-std::unique_ptr<RenderLayer> RenderSymbolLayer::clone() const {
- return std::make_unique<RenderSymbolLayer>(*this);
-}
-
-std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
- assert(false); // Should be calling createLayout() instead.
- return nullptr;
-}
-
-std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters,
- const std::vector<const RenderLayer*>& group,
- const GeometryTileLayer& layer,
- GlyphDependencies& glyphDependencies,
- IconDependencies& iconDependencies) const {
- return std::make_unique<SymbolLayout>(parameters,
- group,
- layer,
- iconDependencies,
- glyphDependencies);
-}
-
-void RenderSymbolLayer::cascade(const CascadeParameters& parameters) {
- unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
-}
-
-void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) {
- evaluated = unevaluated.evaluate(parameters);
-
- auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 ||
- evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0;
- auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 ||
- evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0;
-
- passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0)
- || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0))
- ? RenderPass::Translucent : RenderPass::None;
-}
-
-bool RenderSymbolLayer::hasTransition() const {
- return unevaluated.hasTransition();
-}
-
-style::IconPaintProperties::Evaluated RenderSymbolLayer::iconPaintProperties() const {
- return style::IconPaintProperties::Evaluated {
- evaluated.get<style::IconOpacity>(),
- evaluated.get<style::IconColor>(),
- evaluated.get<style::IconHaloColor>(),
- evaluated.get<style::IconHaloWidth>(),
- evaluated.get<style::IconHaloBlur>(),
- evaluated.get<style::IconTranslate>(),
- evaluated.get<style::IconTranslateAnchor>()
- };
-}
-
-style::TextPaintProperties::Evaluated RenderSymbolLayer::textPaintProperties() const {
- return style::TextPaintProperties::Evaluated {
- evaluated.get<style::TextOpacity>(),
- evaluated.get<style::TextColor>(),
- evaluated.get<style::TextHaloColor>(),
- evaluated.get<style::TextHaloWidth>(),
- evaluated.get<style::TextHaloBlur>(),
- evaluated.get<style::TextTranslate>(),
- evaluated.get<style::TextTranslateAnchor>()
- };
-}
-
-
-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::IconRotationAlignment>(),
- layout_.get<style::IconSize>(),
- evaluated.get<style::IconTranslate>(),
- evaluated.get<style::IconTranslateAnchor>(),
- iconSize,
- 1.0f,
- evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 &&
- evaluated.get<style::IconHaloWidth>().constantOr(1),
- evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0
- };
-}
-
-style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
- return style::SymbolPropertyValues {
- layout_.get<style::TextPitchAlignment>(),
- layout_.get<style::TextRotationAlignment>(),
- layout_.get<style::TextSize>(),
- evaluated.get<style::TextTranslate>(),
- evaluated.get<style::TextTranslateAnchor>(),
- textSize,
- 24.0f,
- evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 &&
- evaluated.get<style::TextHaloWidth>().constantOr(1),
- evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0
- };
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
index ce59186e61..8df31f8d7c 100644
--- a/src/mbgl/renderer/render_tile.cpp
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -1,5 +1,11 @@
#include <mbgl/renderer/render_tile.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 {
@@ -8,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);
}
@@ -35,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::calculateMatrices(const mat4& projMatrix,
- const mat4& projClipMatrix,
- const TransformState& transform) {
+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
- transform.matrixFor(matrix, id);
- transform.matrixFor(nearClippedMatrix, id);
- matrix::multiply(matrix, projMatrix, matrix);
- matrix::multiply(nearClippedMatrix, projClipMatrix, 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 02e8667eec..b498972f5c 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,8 +12,9 @@ namespace mbgl {
class Tile;
class TransformState;
+class PaintParameters;
-class RenderTile {
+class RenderTile final {
public:
RenderTile(UnwrappedTileID id_, Tile& tile_) : id(std::move(id_)), tile(tile_) {}
RenderTile(const RenderTile&) = delete;
@@ -35,14 +37,15 @@ public:
style::TranslateAnchorType anchor,
const TransformState&) const;
- void calculateMatrices(const mat4& projMatrix,
- const mat4& projClipMatrix,
- const TransformState&);
-private:
+ void setMask(TileMask&&);
+ void startRender(PaintParameters&);
+ void finishRender(PaintParameters&);
+
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..e915f5e146
--- /dev/null
+++ b/src/mbgl/renderer/renderer.cpp
@@ -0,0 +1,86 @@
+#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_)
+ : impl(std::make_unique<Impl>(backend, pixelRatio_, fileSource_, scheduler_,
+ contextMode_, std::move(programCacheDir_))) {
+}
+
+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);
+ 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/renderer/renderer_backend.cpp b/src/mbgl/renderer/renderer_backend.cpp
new file mode 100644
index 0000000000..159ef432b3
--- /dev/null
+++ b/src/mbgl/renderer/renderer_backend.cpp
@@ -0,0 +1,69 @@
+#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>
+
+#include <cassert>
+
+namespace mbgl {
+
+RendererBackend::RendererBackend() = default;
+
+gl::Context& RendererBackend::getContext() {
+ assert(BackendScope::exists());
+ std::call_once(initialized, [this] {
+ context = std::make_unique<gl::Context>();
+ context->enableDebugging();
+ context->initializeExtensions(
+ std::bind(&RendererBackend::initializeExtension, this, std::placeholders::_1));
+ });
+ return *context;
+}
+
+PremultipliedImage RendererBackend::readFramebuffer(const Size& size) const {
+ assert(context);
+ return context->readFramebuffer<PremultipliedImage>(size);
+}
+
+void RendererBackend::assumeFramebufferBinding(const gl::FramebufferID fbo) {
+ getContext().bindFramebuffer.setCurrentValue(fbo);
+ if (fbo != ImplicitFramebufferBinding) {
+ assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
+ }
+}
+
+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 RendererBackend::assumeScissorTest(bool enabled) {
+ getContext().scissorTest.setCurrentValue(enabled);
+ assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue());
+}
+
+bool RendererBackend::implicitFramebufferBound() {
+ return getContext().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding;
+}
+
+void RendererBackend::setFramebufferBinding(const gl::FramebufferID fbo) {
+ getContext().bindFramebuffer = fbo;
+ if (fbo != ImplicitFramebufferBinding) {
+ assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
+ }
+}
+
+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 RendererBackend::setScissorTest(bool enabled) {
+ getContext().scissorTest = enabled;
+ assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue());
+}
+
+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..1a828b80a3
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -0,0 +1,742 @@
+#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_)
+ : backend(backend_)
+ , scheduler(scheduler_)
+ , fileSource(fileSource_)
+ , observer(&nullObserver())
+ , contextMode(contextMode_)
+ , pixelRatio(pixelRatio_)
+ , programCacheDir(programCacheDir_)
+ , 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>()) {
+ 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::Still) {
+ // 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,
+ frameHistory,
+ *imageManager,
+ *lineAtlas
+ };
+
+ bool loaded = updateParameters.styleLoaded && isLoaded();
+ if (updateParameters.mode == MapMode::Still && !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 (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(par.y, par.x) < std::tie(pbr.y, pbr.x);
+ });
+ } else {
+ std::sort(sortedTiles.begin(), sortedTiles.end(),
+ [](const auto& a, const auto& b) { return a.get().id < b.get().id; });
+ }
+
+ 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));
+ order.emplace_back(RenderItem { *layer, source });
+ }
+
+ frameHistory.record(parameters.timePoint,
+ parameters.state.getZoom(),
+ parameters.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(parameters.context, "upload");
+
+ parameters.imageManager.upload(parameters.context, 0);
+ parameters.lineAtlas.upload(parameters.context, 0);
+ parameters.frameHistory.upload(parameters.context, 0);
+
+ // Update all clipping IDs + upload buckets.
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ 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.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "clear");
+ parameters.backend.bind();
+ parameters.context.clear((parameters.debugOptions & MapDebugOptions::Overdraw)
+ ? Color::black()
+ : backgroundColor,
+ 1.0f,
+ 0);
+ }
+
+ // - 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() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION))
+ );
+
+ 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());
+ }
+ }
+
+ 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);
+ 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::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() const {
+ if (renderLight.hasTransition()) {
+ return true;
+ }
+
+ for (const auto& entry : renderLayers) {
+ if (entry.second->hasTransition()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+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..30e7f70722
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -0,0 +1,111 @@
+#pragma once
+
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/render_source_observer.hpp>
+#include <mbgl/renderer/render_light.hpp>
+#include <mbgl/renderer/frame_history.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/source.hpp>
+#include <mbgl/style/layer.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/map/zoom_history.hpp>
+#include <mbgl/map/mode.hpp>
+#include <mbgl/text/glyph_manager_observer.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 Renderer::Impl : public GlyphManagerObserver,
+ public RenderSourceObserver{
+public:
+ Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode,
+ const optional<std::string> programCacheDir);
+ ~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;
+
+ void onLowMemory();
+ void dumDebugLogs();
+
+private:
+ bool isLoaded() const;
+ bool hasTransitions() const;
+
+ RenderSource* getRenderSource(const std::string& id) const;
+
+ RenderLayer* getRenderLayer(const std::string& id);
+ const RenderLayer* getRenderLayer(const std::string& id) 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;
+
+ 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;
+ FrameHistory frameHistory;
+ 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;
+
+ bool contextLost = 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_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp
index 2b1eeea73b..504db78ea3 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -1,6 +1,8 @@
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/render_tile.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>
@@ -9,81 +11,87 @@ namespace mbgl {
using namespace style;
-RenderGeoJSONSource::RenderGeoJSONSource(const style::GeoJSONSource::Impl& impl_)
- : RenderSource(impl_),
- impl(impl_) {
+RenderGeoJSONSource::RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl> impl_)
+ : RenderSource(impl_) {
tilePyramid.setObserver(this);
}
-bool RenderGeoJSONSource::isLoaded() const {
- return tilePyramid.isLoaded();
-}
-
-void RenderGeoJSONSource::invalidateTiles() {
- tilePyramid.invalidateTiles();
-}
-
-void RenderGeoJSONSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) {
- generator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(projMatrix, clipMatrix, transform);
+const style::GeoJSONSource::Impl& RenderGeoJSONSource::impl() const {
+ return static_cast<const style::GeoJSONSource::Impl&>(*baseImpl);
}
-void RenderGeoJSONSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+bool RenderGeoJSONSource::isLoaded() const {
+ return tilePyramid.isLoaded();
}
-std::map<UnwrappedTileID, RenderTile>& RenderGeoJSONSource::getRenderTiles() {
- return tilePyramid.getRenderTiles();
-}
+void RenderGeoJSONSource::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_);
-void RenderGeoJSONSource::updateTiles(const TileParameters& parameters) {
- GeoJSONData* data_ = impl.getData();
+ enabled = needsRendering;
- if (!data_) {
- return;
- }
+ GeoJSONData* data_ = impl().getData();
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));
+ }
+ }
}
}
- tilePyramid.updateTiles(parameters,
- SourceType::GeoJSON,
- util::tileSize,
- impl.getZoomRange(),
- [&] (const OverscaledTileID& tileID) {
- return std::make_unique<GeoJSONTile>(tileID, impl.id, parameters, data->getTile(tileID.canonical));
- });
+ if (!data) {
+ tilePyramid.tiles.clear();
+ tilePyramid.renderTiles.clear();
+ return;
+ }
+
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::GeoJSON,
+ util::tileSize,
+ impl().getZoomRange(),
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<GeoJSONTile>(tileID, impl().id, parameters, data->getTile(tileID.canonical));
+ });
}
-void RenderGeoJSONSource::removeTiles() {
- tilePyramid.removeTiles();
+void RenderGeoJSONSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderGeoJSONSource::reloadTiles() {
- tilePyramid.reloadTiles();
+void RenderGeoJSONSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
+}
+
+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 RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, options);
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
}
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 262ab29276..72ab4879ef 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.hpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.hpp
@@ -12,48 +12,43 @@ class GeoJSONData;
class RenderGeoJSONSource : public RenderSource {
public:
- RenderGeoJSONSource(const style::GeoJSONSource::Impl&);
+ RenderGeoJSONSource(Immutable<style::GeoJSONSource::Impl>);
bool isLoaded() const final;
- // Called when the camera has changed. May load new tiles, unload obsolete tiles, or
- // trigger re-placement of existing complete tiles.
- void updateTiles(const TileParameters&) final;
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
- // Removes all tiles (by putting them into the cache).
- void removeTiles() final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- // Remove all tiles and clear the cache.
- void invalidateTiles() final;
-
- // Request that all loaded tiles re-run the layout operation on the existing source
- // data with fresh style information.
- void reloadTiles() final;
-
- void startRender(algorithm::ClipIDGenerator&,
- const mat4& projMatrix,
- const mat4& clipMatrix,
- const TransformState&) final;
- void finishRender(Painter&) 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 std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
private:
- const style::GeoJSONSource::Impl& impl;
+ 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;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp
new file mode 100644
index 0000000000..9140e01711
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_image_source.cpp
@@ -0,0 +1,214 @@
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/math/log2.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.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_) {
+}
+
+RenderImageSource::~RenderImageSource() = default;
+
+const style::ImageSource::Impl& RenderImageSource::impl() const {
+ return static_cast<const style::ImageSource::Impl&>(*baseImpl);
+}
+
+bool RenderImageSource::isLoaded() const {
+ return !!bucket;
+}
+
+void RenderImageSource::startRender(PaintParameters& parameters) {
+ if (!isLoaded()) {
+ return;
+ }
+
+ matrices.clear();
+
+ for (size_t i = 0; i < tileIds.size(); i++) {
+ mat4 matrix;
+ matrix::identity(matrix);
+ parameters.state.matrixFor(matrix, tileIds[i]);
+ matrix::multiply(matrix, parameters.projMatrix, matrix);
+ matrices.push_back(matrix);
+ }
+
+ if (bucket->needsUpload()) {
+ bucket->upload(parameters.context);
+ }
+}
+
+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) {
+ 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 std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&) const {
+ return std::unordered_map<std::string, std::vector<Feature>> {};
+}
+
+std::vector<Feature> RenderImageSource::querySourceFeatures(const SourceQueryOptions&) const {
+ return {};
+}
+
+void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
+ const std::vector<Immutable<Layer::Impl>>&,
+ const bool needsRendering,
+ const bool,
+ const TileParameters& parameters) {
+ enabled = needsRendering;
+ if (!needsRendering) {
+ return;
+ }
+
+ auto transformState = parameters.transformState;
+ std::swap(baseImpl, baseImpl_);
+
+ auto coords = impl().getCoordinates();
+ std::shared_ptr<PremultipliedImage> image = impl().getImage();
+
+ if (!image || !image->valid()) {
+ enabled = false;
+ return;
+ }
+
+ auto size = transformState.getSize();
+ const double viewportHeight = size.height;
+
+ // Compute the screen coordinates at wrap=0 for the given LatLng
+ ScreenCoordinate nePixel = { -INFINITY, -INFINITY };
+ ScreenCoordinate swPixel = { INFINITY, INFINITY };
+
+ for (LatLng latLng : coords) {
+ ScreenCoordinate pixel = transformState.latLngToScreenCoordinate(latLng);
+ swPixel.x = std::min(swPixel.x, pixel.x);
+ nePixel.x = std::max(nePixel.x, pixel.x);
+ swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y);
+ nePixel.y = std::max(nePixel.y, viewportHeight - pixel.y);
+ }
+ const double width = nePixel.x - swPixel.x;
+ const double height = nePixel.y - swPixel.y;
+
+ // Don't bother drawing the ImageSource unless it occupies >4 screen pixels
+ enabled = (width * height > 4);
+ if (!enabled) {
+ return;
+ }
+
+ // Calculate the optimum zoom level to determine the tile ids to use for transforms
+ double minScale = INFINITY;
+ double scaleX = double(size.width) / width;
+ double scaleY = double(size.height) / height;
+ minScale = util::min(scaleX, scaleY);
+ double zoom = transformState.getZoom() + util::log2(minScale);
+ zoom = std::floor(util::clamp(zoom, transformState.getMinZoom(), transformState.getMaxZoom()));
+ auto imageBounds = LatLngBounds::hull(coords[0], coords[1]);
+ imageBounds.extend(coords[2]);
+ imageBounds.extend(coords[3]);
+ auto tileCover = util::tileCover(imageBounds, zoom);
+ tileIds.clear();
+ tileIds.push_back(tileCover[0]);
+ bool hasVisibleTile = false;
+
+ // 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);
+ geomCoords.push_back(gc);
+ }
+ 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 }, { util::EXTENT, 0 }));
+ bucket->vertices.emplace_back(
+ RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT }));
+ bucket->vertices.emplace_back(
+ 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);
+
+ bucket->segments.emplace_back(0, 0, 4, 6);
+}
+
+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");
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp
new file mode 100644
index 0000000000..7b69d09fa7
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_image_source.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/style/sources/image_source_impl.hpp>
+
+namespace mbgl {
+
+class RasterBucket;
+
+class RenderImageSource : public RenderSource {
+public:
+ RenderImageSource(Immutable<style::ImageSource::Impl>);
+ ~RenderImageSource() override;
+
+ bool isLoaded() const final;
+
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
+
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
+
+ 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 std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const final;
+
+ std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final;
+
+ void onLowMemory() final {
+ }
+ void dumpDebugLogs() const final;
+
+private:
+ friend class RenderRasterLayer;
+
+ const style::ImageSource::Impl& impl() const;
+
+ std::vector<UnwrappedTileID> tileIds;
+ std::unique_ptr<RasterBucket> bucket;
+ std::vector<mat4> matrices;
+};
+
+template <>
+inline bool RenderSource::is<RenderImageSource>() const {
+ return baseImpl->type == 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 c5a29eebf5..bcd719365d 100644
--- a/src/mbgl/renderer/sources/render_raster_source.cpp
+++ b/src/mbgl/renderer/sources/render_raster_source.cpp
@@ -1,39 +1,35 @@
#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 {
using namespace style;
-RenderRasterSource::RenderRasterSource(const style::RasterSource::Impl& impl_)
- : RenderSource(impl_),
- impl(impl_) {
+RenderRasterSource::RenderRasterSource(Immutable<style::RasterSource::Impl> impl_)
+ : RenderSource(impl_) {
tilePyramid.setObserver(this);
}
-bool RenderRasterSource::isLoaded() const {
- return tilePyramid.isLoaded();
-}
-
-void RenderRasterSource::invalidateTiles() {
- tilePyramid.invalidateTiles();
+const style::RasterSource::Impl& RenderRasterSource::impl() const {
+ return static_cast<const style::RasterSource::Impl&>(*baseImpl);
}
-void RenderRasterSource::startRender(algorithm::ClipIDGenerator&, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) {
- tilePyramid.startRender(projMatrix, clipMatrix, transform);
+bool RenderRasterSource::isLoaded() const {
+ return tilePyramid.isLoaded();
}
-void RenderRasterSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
-}
+void RenderRasterSource::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_);
-std::map<UnwrappedTileID, RenderTile>& RenderRasterSource::getRenderTiles() {
- return tilePyramid.getRenderTiles();
-}
+ enabled = needsRendering;
-void RenderRasterSource::updateTiles(const TileParameters& parameters) {
- optional<Tileset> tileset = impl.getTileset();
+ optional<Tileset> tileset = impl().getTileset();
if (!tileset) {
return;
@@ -41,41 +37,51 @@ void RenderRasterSource::updateTiles(const TileParameters& parameters) {
if (tileURLTemplates != tileset->tiles) {
tileURLTemplates = tileset->tiles;
- tilePyramid.invalidateTiles();
+
+ // TODO: this removes existing buckets, and will cause flickering.
+ // Should instead refresh tile data in place.
+ tilePyramid.tiles.clear();
+ tilePyramid.renderTiles.clear();
+ tilePyramid.cache.clear();
}
- tilePyramid.updateTiles(parameters,
- SourceType::Raster,
- impl.getTileSize(),
- tileset->zoomRange,
- [&] (const OverscaledTileID& tileID) {
- return std::make_unique<RasterTile>(tileID, parameters, *tileset);
- });
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::Raster,
+ impl().getTileSize(),
+ tileset->zoomRange,
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<RasterTile>(tileID, parameters, *tileset);
+ });
+}
+
+void RenderRasterSource::startRender(PaintParameters& parameters) {
+ algorithm::updateTileMasks(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderRasterSource::removeTiles() {
- tilePyramid.removeTiles();
+void RenderRasterSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-void RenderRasterSource::reloadTiles() {
- tilePyramid.reloadTiles();
+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 std::vector<const RenderLayer*>&,
const RenderedQueryOptions&) const {
- return {};
+ 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 5690ba80ea..01de812309 100644
--- a/src/mbgl/renderer/sources/render_raster_source.hpp
+++ b/src/mbgl/renderer/sources/render_raster_source.hpp
@@ -8,48 +8,43 @@ namespace mbgl {
class RenderRasterSource : public RenderSource {
public:
- RenderRasterSource(const style::RasterSource::Impl&);
+ RenderRasterSource(Immutable<style::RasterSource::Impl>);
bool isLoaded() const final;
- // Called when the camera has changed. May load new tiles, unload obsolete tiles, or
- // trigger re-placement of existing complete tiles.
- void updateTiles(const TileParameters&) final;
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
- // Removes all tiles (by putting them into the cache).
- void removeTiles() final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- // Remove all tiles and clear the cache.
- void invalidateTiles() final;
-
- // Request that all loaded tiles re-run the layout operation on the existing source
- // data with fresh style information.
- void reloadTiles() final;
-
- void startRender(algorithm::ClipIDGenerator&,
- const mat4& projMatrix,
- const mat4& clipMatrix,
- const TransformState&) final;
- void finishRender(Painter&) 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 std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
private:
- const style::RasterSource::Impl& impl;
+ const style::RasterSource::Impl& impl() const;
+
TilePyramid tilePyramid;
optional<std::vector<std::string>> tileURLTemplates;
};
+template <>
+inline bool RenderSource::is<RenderRasterSource>() const {
+ return baseImpl->type == 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 0db4698a81..ca3071c6b0 100644
--- a/src/mbgl/renderer/sources/render_vector_source.cpp
+++ b/src/mbgl/renderer/sources/render_vector_source.cpp
@@ -1,5 +1,6 @@
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/tile/vector_tile.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
@@ -9,35 +10,29 @@ namespace mbgl {
using namespace style;
-RenderVectorSource::RenderVectorSource(const style::VectorSource::Impl& impl_)
- : RenderSource(impl_),
- impl(impl_) {
+RenderVectorSource::RenderVectorSource(Immutable<style::VectorSource::Impl> impl_)
+ : RenderSource(impl_) {
tilePyramid.setObserver(this);
}
-bool RenderVectorSource::isLoaded() const {
- return tilePyramid.isLoaded();
+const style::VectorSource::Impl& RenderVectorSource::impl() const {
+ return static_cast<const style::VectorSource::Impl&>(*baseImpl);
}
-void RenderVectorSource::invalidateTiles() {
- tilePyramid.invalidateTiles();
+bool RenderVectorSource::isLoaded() const {
+ return tilePyramid.isLoaded();
}
-void RenderVectorSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) {
- generator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(projMatrix, clipMatrix, transform);
-}
+void RenderVectorSource::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_);
-void RenderVectorSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
-}
+ enabled = needsRendering;
-std::map<UnwrappedTileID, RenderTile>& RenderVectorSource::getRenderTiles() {
- return tilePyramid.getRenderTiles();
-}
-
-void RenderVectorSource::updateTiles(const TileParameters& parameters) {
- optional<Tileset> tileset = impl.getTileset();
+ optional<Tileset> tileset = impl().getTileset();
if (!tileset) {
return;
@@ -45,41 +40,51 @@ void RenderVectorSource::updateTiles(const TileParameters& parameters) {
if (tileURLTemplates != tileset->tiles) {
tileURLTemplates = tileset->tiles;
- tilePyramid.invalidateTiles();
+
+ // TODO: this removes existing buckets, and will cause flickering.
+ // Should instead refresh tile data in place.
+ tilePyramid.tiles.clear();
+ tilePyramid.renderTiles.clear();
+ tilePyramid.cache.clear();
}
- tilePyramid.updateTiles(parameters,
- SourceType::Vector,
- util::tileSize,
- tileset->zoomRange,
- [&] (const OverscaledTileID& tileID) {
- return std::make_unique<VectorTile>(tileID, impl.id, parameters, *tileset);
- });
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::Vector,
+ util::tileSize,
+ tileset->zoomRange,
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<VectorTile>(tileID, impl().id, parameters, *tileset);
+ });
}
-void RenderVectorSource::removeTiles() {
- tilePyramid.removeTiles();
+void RenderVectorSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderVectorSource::reloadTiles() {
- tilePyramid.reloadTiles();
+void RenderVectorSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
+}
+
+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 std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, options);
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
}
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 36d75e0982..5e5c6d1108 100644
--- a/src/mbgl/renderer/sources/render_vector_source.hpp
+++ b/src/mbgl/renderer/sources/render_vector_source.hpp
@@ -8,48 +8,43 @@ namespace mbgl {
class RenderVectorSource : public RenderSource {
public:
- RenderVectorSource(const style::VectorSource::Impl&);
+ RenderVectorSource(Immutable<style::VectorSource::Impl>);
bool isLoaded() const final;
- // Called when the camera has changed. May load new tiles, unload obsolete tiles, or
- // trigger re-placement of existing complete tiles.
- void updateTiles(const TileParameters&) final;
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
- // Removes all tiles (by putting them into the cache).
- void removeTiles() final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- // Remove all tiles and clear the cache.
- void invalidateTiles() final;
-
- // Request that all loaded tiles re-run the layout operation on the existing source
- // data with fresh style information.
- void reloadTiles() final;
-
- void startRender(algorithm::ClipIDGenerator&,
- const mat4& projMatrix,
- const mat4& clipMatrix,
- const TransformState&) final;
- void finishRender(Painter&) 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 std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
private:
- const style::VectorSource::Impl& impl;
+ const style::VectorSource::Impl& impl() const;
+
TilePyramid tilePyramid;
optional<std::vector<std::string>> tileURLTemplates;
};
+template <>
+inline bool RenderSource::is<RenderVectorSource>() const {
+ return baseImpl->type == SourceType::Vector;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/style_diff.cpp b/src/mbgl/renderer/style_diff.cpp
new file mode 100644
index 0000000000..0017280310
--- /dev/null
+++ b/src/mbgl/renderer/style_diff.cpp
@@ -0,0 +1,79 @@
+#include <mbgl/renderer/style_diff.hpp>
+#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/util/immutable.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/longest_common_subsequence.hpp>
+
+namespace mbgl {
+
+template <class T, class Eq>
+StyleDifference<T> diff(const Immutable<std::vector<T>>& a,
+ const Immutable<std::vector<T>>& b,
+ const Eq& eq) {
+ StyleDifference<T> result;
+
+ if (a == b) {
+ return result;
+ }
+
+ std::vector<T> lcs;
+
+ longest_common_subsequence(a->begin(), a->end(), b->begin(), b->end(), std::back_inserter(lcs), eq);
+
+ auto aIt = a->begin();
+ auto bIt = b->begin();
+ auto lIt = lcs.begin();
+
+ while (aIt != a->end() || bIt != b->end()) {
+ if (aIt != a->end() && (lIt == lcs.end() || !eq(*lIt, *aIt))) {
+ result.removed.emplace((*aIt)->id, *aIt);
+ aIt++;
+ } else if (bIt != b->end() && (lIt == lcs.end() || !eq(*lIt, *bIt))) {
+ result.added.emplace((*bIt)->id, *bIt);
+ bIt++;
+ } else {
+ if (aIt->get() != bIt->get()) {
+ result.changed.emplace((*bIt)->id, StyleChange<T> { *aIt, *bIt });
+ }
+ aIt++;
+ bIt++;
+ lIt++;
+ }
+ }
+
+ return result;
+}
+
+ImageDifference diffImages(const Immutable<std::vector<ImmutableImage>>& a,
+ const Immutable<std::vector<ImmutableImage>>& b) {
+ return diff(a, b, [] (const ImmutableImage& lhs, const ImmutableImage& rhs) {
+ return lhs->id == rhs->id;
+ });
+}
+
+SourceDifference diffSources(const Immutable<std::vector<ImmutableSource>>& a,
+ const Immutable<std::vector<ImmutableSource>>& b) {
+ return diff(a, b, [] (const ImmutableSource& lhs, const ImmutableSource& rhs) {
+ return std::tie(lhs->id, lhs->type)
+ == std::tie(rhs->id, rhs->type);
+ });
+}
+
+LayerDifference diffLayers(const Immutable<std::vector<ImmutableLayer>>& a,
+ const Immutable<std::vector<ImmutableLayer>>& b) {
+ return diff(a, b, [] (const ImmutableLayer& lhs, const ImmutableLayer& rhs) {
+ return std::tie(lhs->id, lhs->type)
+ == std::tie(rhs->id, rhs->type);
+ });
+}
+
+bool hasLayoutDifference(const LayerDifference& layerDiff, const std::string& layerID) {
+ if (layerDiff.added.count(layerID))
+ return true;
+ const auto it = layerDiff.changed.find(layerID);
+ if (it == layerDiff.changed.end())
+ return false;
+ return it->second.before->hasLayoutDifference(*it->second.after);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/style_diff.hpp b/src/mbgl/renderer/style_diff.hpp
new file mode 100644
index 0000000000..a5b42fc662
--- /dev/null
+++ b/src/mbgl/renderer/style_diff.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/util/immutable.hpp>
+#include <mbgl/util/variant.hpp>
+
+#include <unordered_map>
+
+namespace mbgl {
+
+template <class T>
+class StyleChange {
+public:
+ T before;
+ T after;
+};
+
+template <class T>
+class StyleDifference {
+public:
+ std::unordered_map<std::string, T> added;
+ std::unordered_map<std::string, T> removed;
+ std::unordered_map<std::string, StyleChange<T>> changed;
+};
+
+using ImmutableImage = Immutable<style::Image::Impl>;
+using ImageDifference = StyleDifference<ImmutableImage>;
+
+ImageDifference diffImages(const Immutable<std::vector<ImmutableImage>>&,
+ const Immutable<std::vector<ImmutableImage>>&);
+
+using ImmutableSource = Immutable<style::Source::Impl>;
+using SourceDifference = StyleDifference<ImmutableSource>;
+
+SourceDifference diffSources(const Immutable<std::vector<ImmutableSource>>&,
+ const Immutable<std::vector<ImmutableSource>>&);
+
+using ImmutableLayer = Immutable<style::Layer::Impl>;
+using LayerDifference = StyleDifference<ImmutableLayer>;
+
+LayerDifference diffLayers(const Immutable<std::vector<ImmutableLayer>>&,
+ const Immutable<std::vector<ImmutableLayer>>&);
+
+bool hasLayoutDifference(const LayerDifference&, const std::string& layerID);
+
+} // 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 8f04baaec5..665c7490d2 100644
--- a/src/mbgl/renderer/tile_parameters.hpp
+++ b/src/mbgl/renderer/tile_parameters.hpp
@@ -8,40 +8,21 @@ class TransformState;
class Scheduler;
class FileSource;
class AnnotationManager;
-
-namespace style {
-class Style;
-} // namespace style
+class ImageManager;
+class GlyphManager;
class TileParameters {
public:
- TileParameters(float pixelRatio_,
- MapDebugOptions debugOptions_,
- const TransformState& transformState_,
- Scheduler& workerScheduler_,
- FileSource& fileSource_,
- const MapMode mode_,
- AnnotationManager& annotationManager_,
- style::Style& style_)
- : pixelRatio(pixelRatio_),
- debugOptions(debugOptions_),
- transformState(transformState_),
- workerScheduler(workerScheduler_),
- fileSource(fileSource_),
- mode(mode_),
- annotationManager(annotationManager_),
- style(style_) {}
-
- float pixelRatio;
- MapDebugOptions debugOptions;
+ const float pixelRatio;
+ const MapDebugOptions debugOptions;
const TransformState& transformState;
Scheduler& workerScheduler;
FileSource& fileSource;
const MapMode mode;
AnnotationManager& annotationManager;
-
- // TODO: remove
- style::Style& style;
+ 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 144afcb4f6..6cd9bd9ebd 100644
--- a/src/mbgl/renderer/tile_pyramid.cpp
+++ b/src/mbgl/renderer/tile_pyramid.cpp
@@ -1,10 +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>
@@ -39,50 +39,77 @@ bool TilePyramid::isLoaded() const {
return true;
}
-void TilePyramid::invalidateTiles() {
- tiles.clear();
- renderTiles.clear();
- cache.clear();
-}
-
-void TilePyramid::startRender(const mat4& projMatrix,
- const mat4& clipMatrix,
- const TransformState& transform) {
- for (auto& pair : renderTiles) {
- auto& tile = pair.second;
- tile.calculateMatrices(projMatrix, clipMatrix, transform);
+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::updateTiles(const TileParameters& parameters,
- const SourceType type,
- const uint16_t tileSize,
- const Range<uint8_t> zoomRange,
- std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) {
+void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers,
+ const bool needsRendering,
+ const bool needsRelayout,
+ const TileParameters& parameters,
+ const SourceType type,
+ const uint16_t tileSize,
+ const Range<uint8_t> zoomRange,
+ std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile) {
+ // If we need a relayout, abandon any cached tiles; they're now stale.
+ if (needsRelayout) {
+ cache.clear();
+ }
+
+ // If we're not going to render anything, move our existing tiles into
+ // the cache (if they're not stale) or abandon them, and return.
+ if (!needsRendering) {
+ if (!needsRelayout) {
+ for (auto& entry : tiles) {
+ cache.add(entry.first, std::move(entry.second));
+ }
+ }
+
+ tiles.clear();
+ renderTiles.clear();
+
+ return;
+ }
+
// 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) {
tileZoom = idealZoom;
+
+ // FIXME: Prefetching is only enabled for raster
+ // tiles until we fix #7026.
+
+ // Request lower zoom level tiles (if configure to do so) in an attempt
+ // 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);
@@ -95,8 +122,13 @@ void TilePyramid::updateTiles(const TileParameters& parameters,
std::set<OverscaledTileID> retain;
auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void {
- retain.emplace(tile.id);
- tile.setNecessity(necessity);
+ if (retain.emplace(tile.id).second) {
+ tile.setNecessity(necessity);
+ }
+
+ if (needsRelayout) {
+ tile.setLayers(layers);
+ }
};
auto getTileFn = [&](const OverscaledTileID& tileID) -> Tile* {
auto it = tiles.find(tileID);
@@ -108,6 +140,7 @@ void TilePyramid::updateTiles(const TileParameters& parameters,
tile = createTile(tileID);
if (tile) {
tile->setObserver(observer);
+ tile->setLayers(layers);
}
}
if (!tile) {
@@ -116,10 +149,16 @@ void TilePyramid::updateTiles(const TileParameters& parameters,
return tiles.emplace(tileID, std::move(tile)).first->second.get();
};
auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) {
- renderTiles.emplace(tileID, RenderTile{ tileID, tile });
+ renderTiles.emplace_back(tileID, tile);
};
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);
@@ -132,54 +171,41 @@ void TilePyramid::updateTiles(const TileParameters& parameters,
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()) {
+ if (retainIt == retain.end() || tilesIt->first < *retainIt) {
+ if (!needsRelayout) {
+ tilesIt->second->setNecessity(Tile::Necessity::Optional);
+ cache.add(tilesIt->first, std::move(tilesIt->second));
+ }
+ tiles.erase(tilesIt++);
+ } else {
+ if (!(*retainIt < tilesIt->first)) {
+ ++tilesIt;
+ }
+ ++retainIt;
}
- ++retainIt;
}
}
-}
-
-void TilePyramid::removeTiles() {
- renderTiles.clear();
- if (!tiles.empty()) {
- removeStaleTiles({});
- }
-}
-
-void TilePyramid::reloadTiles() {
- cache.clear();
for (auto& pair : tiles) {
- pair.second->redoLayout();
+ const PlacementConfig config { parameters.transformState.getAngle(),
+ parameters.transformState.getPitch(),
+ parameters.transformState.getCameraToCenterDistance(),
+ parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()),
+ parameters.debugOptions & MapDebugOptions::Collision };
+
+ pair.second->setPlacementConfig(config);
}
}
std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
@@ -195,18 +221,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;
@@ -226,6 +248,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
renderTile.tile.queryRenderedFeatures(result,
tileSpaceQueryGeometry,
transformState,
+ layers,
options);
}
diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp
index b51c5342de..73a8d34c1c 100644
--- a/src/mbgl/renderer/tile_pyramid.hpp
+++ b/src/mbgl/renderer/tile_pyramid.hpp
@@ -5,6 +5,7 @@
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/tile_cache.hpp>
#include <mbgl/style/types.hpp>
+#include <mbgl/style/layer_impl.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/feature.hpp>
@@ -17,9 +18,10 @@
namespace mbgl {
-class Painter;
+class PaintParameters;
class TransformState;
class RenderTile;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
class TileParameters;
@@ -31,34 +33,24 @@ public:
bool isLoaded() const;
- // Called when the camera has changed. May load new tiles, unload obsolete tiles, or
- // trigger re-placement of existing complete tiles.
- void updateTiles(const TileParameters&,
- SourceType type,
- uint16_t tileSize,
- Range<uint8_t> zoomRange,
- std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile);
+ void update(const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&,
+ SourceType type,
+ uint16_t tileSize,
+ Range<uint8_t> zoomRange,
+ std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile);
- // Removes all tiles (by putting them into the cache).
- void removeTiles();
+ void startRender(PaintParameters&);
+ void finishRender(PaintParameters&);
- // Remove all tiles and clear the cache.
- void invalidateTiles();
-
- // Request that all loaded tiles re-run the layout operation on the existing source
- // data with fresh style information.
- void reloadTiles();
-
- void startRender(const mat4& projMatrix,
- const mat4& clipMatrix,
- const TransformState&);
- void finishRender(Painter&);
-
- 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 std::vector<const RenderLayer*>&,
const RenderedQueryOptions& options) const;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const;
@@ -71,12 +63,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/cascade_parameters.hpp b/src/mbgl/renderer/transition_parameters.hpp
index 4096cc5a6b..c47aa2e35f 100644
--- a/src/mbgl/renderer/cascade_parameters.hpp
+++ b/src/mbgl/renderer/transition_parameters.hpp
@@ -1,16 +1,14 @@
#pragma once
#include <mbgl/util/chrono.hpp>
-#include <mbgl/style/class_dictionary.hpp>
#include <mbgl/style/transition_options.hpp>
#include <vector>
namespace mbgl {
-class CascadeParameters {
+class TransitionParameters {
public:
- std::vector<style::ClassID> classes;
TimePoint now;
style::TransitionOptions transition;
};
diff --git a/src/mbgl/renderer/transitioning_property.hpp b/src/mbgl/renderer/transitioning_property.hpp
deleted file mode 100644
index c211ccf116..0000000000
--- a/src/mbgl/renderer/transitioning_property.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-#pragma once
-
-#include <mbgl/style/property_value.hpp>
-#include <mbgl/style/data_driven_property_value.hpp>
-#include <mbgl/style/transition_options.hpp>
-#include <mbgl/util/interpolate.hpp>
-
-#include <utility>
-
-namespace mbgl {
-
-template <class Value>
-class TransitioningProperty {
-public:
- TransitioningProperty() = default;
-
- TransitioningProperty(Value value_,
- TransitioningProperty<Value> prior_,
- style::TransitionOptions transition,
- TimePoint now)
- : begin(now + transition.delay.value_or(Duration::zero())),
- end(begin + transition.duration.value_or(Duration::zero())),
- value(std::move(value_)) {
- if (transition.isDefined()) {
- prior = { std::move(prior_) };
- }
- }
-
- template <class Evaluator>
- auto evaluate(const Evaluator& evaluator, TimePoint now) {
- auto finalValue = value.evaluate(evaluator);
- if (!prior) {
- // No prior value.
- return finalValue;
- } else if (now >= end) {
- // Transition from prior value is now complete.
- prior = {};
- return finalValue;
- } else if (value.isDataDriven()) {
- // Transitions to data-driven properties are not supported.
- // We snap immediately to the data-driven value so that, when we perform layout,
- // we see the data-driven function and can use it to populate vertex buffers.
- prior = {};
- return finalValue;
- } else if (now < begin) {
- // Transition hasn't started yet.
- return prior->get().evaluate(evaluator, now);
- } else {
- // Interpolate between recursively-calculated prior value and final.
- float t = std::chrono::duration<float>(now - begin) / (end - begin);
- return util::interpolate(prior->get().evaluate(evaluator, now), finalValue,
- util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
- }
- }
-
- bool hasTransition() const {
- return bool(prior);
- }
-
- bool isUndefined() const {
- return value.isUndefined();
- }
-
- const Value& getValue() const {
- return value;
- }
-
-private:
- optional<mapbox::util::recursive_wrapper<TransitioningProperty<Value>>> prior;
- TimePoint begin;
- TimePoint end;
- Value value;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp
index ae54ac09e7..b54abc050d 100644
--- a/src/mbgl/renderer/update_parameters.hpp
+++ b/src/mbgl/renderer/update_parameters.hpp
@@ -1,26 +1,43 @@
#pragma once
#include <mbgl/map/mode.hpp>
-#include <mbgl/map/update.hpp>
+#include <mbgl/map/transform_state.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 TransformState;
-class Scheduler;
-class FileSource;
class AnnotationManager;
class UpdateParameters {
public:
+ const bool styleLoaded;
const MapMode mode;
- const Update updateFlags;
const float pixelRatio;
const MapDebugOptions debugOptions;
const TimePoint timePoint;
- const TransformState& transformState;
- Scheduler& scheduler;
- FileSource& fileSource;
+ const TransformState transformState;
+
+ const std::string glyphURL;
+ const bool spriteLoaded;
+ const style::TransitionOptions transitionOptions;
+ const Immutable<style::Light::Impl> light;
+ const Immutable<std::vector<Immutable<style::Image::Impl>>> images;
+ const Immutable<std::vector<Immutable<style::Source::Impl>>> sources;
+ const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers;
+
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..07fa94e338 100644
--- a/src/mbgl/shaders/collision_box.cpp
+++ b/src/mbgl/shaders/collision_box.cpp
@@ -8,44 +8,74 @@ 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;
uniform mat4 u_matrix;
uniform float u_scale;
+uniform float u_pitch;
+uniform float u_collision_y_stretch;
+uniform float u_camera_to_center_distance;
varying float v_max_zoom;
varying float v_placement_zoom;
+varying float v_perspective_zoom_adjust;
+varying vec2 v_fade_tex;
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 = 1.0 + 0.5 * ((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0);
+
+ highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch));
+ highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch);
+
+ gl_Position = u_matrix * vec4(a_pos + a_extrude * collision_perspective_ratio * collision_adjustment / u_scale, 0.0, 1.0);
v_max_zoom = a_data.x;
v_placement_zoom = a_data.y;
+
+ v_perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0);
+ v_fade_tex = vec2((v_placement_zoom + v_perspective_zoom_adjust) / 255.0, 0.0);
}
)MBGL_SHADER";
const char* collision_box::fragmentSource = R"MBGL_SHADER(
uniform float u_zoom;
+// u_maxzoom is derived from the maximum scale considered by the CollisionTile
+// Labels with placement zoom greater than this value will not be placed,
+// regardless of perspective effects.
uniform float u_maxzoom;
+uniform sampler2D u_fadetexture;
+// v_max_zoom is a collision-box-specific value that controls when line-following
+// collision boxes are used.
varying float v_max_zoom;
varying float v_placement_zoom;
+varying float v_perspective_zoom_adjust;
+varying vec2 v_fade_tex;
void main() {
float alpha = 0.5;
+ // Green = no collisions, label is showing
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0) * alpha;
- if (v_placement_zoom > u_zoom) {
+ // Red = collision, label hidden
+ if (texture2D(u_fadetexture, v_fade_tex).a < 1.0) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
}
- if (u_zoom >= v_max_zoom) {
+ // Faded black = this collision box is not used at this zoom (for curved labels)
+ if (u_zoom >= v_max_zoom + v_perspective_zoom_adjust) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * alpha * 0.25;
}
+ // Faded blue = the placement scale for this label is beyond the CollisionTile
+ // max scale, so it's impossible for this label to show without collision detection
+ // being run again (the label's glyphs haven't even been added to the symbol bucket)
if (v_placement_zoom >= u_maxzoom) {
gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0) * alpha * 0.2;
}
diff --git a/src/mbgl/shaders/debug.cpp b/src/mbgl/shaders/debug.cpp
index d39dcf25be..d18f3be5d1 100644
--- a/src/mbgl/shaders/debug.cpp
+++ b/src/mbgl/shaders/debug.cpp
@@ -12,7 +12,12 @@ attribute vec2 a_pos;
uniform mat4 u_matrix;
void main() {
- gl_Position = u_matrix * vec4(a_pos, step(32767.0, a_pos.x), 1);
+ // We are using Int16 for texture position coordinates to give us enough precision for
+ // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer
+ // as an arbitrarily high number to preserve adequate precision when rendering.
+ // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates,
+ // so math for modifying either is consistent.
+ gl_Position = u_matrix * vec4(a_pos, step(8192.0, a_pos.x), 1);
}
)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..817f73391c 100644
--- a/src/mbgl/shaders/fill_extrusion.cpp
+++ b/src/mbgl/shaders/fill_extrusion.cpp
@@ -27,6 +27,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 +37,7 @@ uniform lowp float u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
uniform lowp float a_color_t;
attribute highp vec4 a_color;
@@ -44,26 +46,30 @@ 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
+
base = max(0.0, base);
height = max(0.0, height);
@@ -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 66c24b1bb0..d3e5eef1bf 100644
--- a/src/mbgl/shaders/fill_extrusion_pattern.cpp
+++ b/src/mbgl/shaders/fill_extrusion_pattern.cpp
@@ -39,6 +39,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,20 +48,23 @@ 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
+
base = max(0.0, base);
height = max(0.0, height);
@@ -93,6 +97,7 @@ uniform vec2 u_pattern_tl_a;
uniform vec2 u_pattern_br_a;
uniform vec2 u_pattern_tl_b;
uniform vec2 u_pattern_br_b;
+uniform vec2 u_texsize;
uniform float u_mix;
uniform sampler2D u_image;
@@ -108,28 +113,32 @@ 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_pattern_br_a, imagecoord);
+ vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
vec2 imagecoord_b = mod(v_pos_b, 1.0);
- vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b);
+ vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b);
vec4 color2 = texture2D(u_image, pos2);
vec4 mixedColor = mix(color1, color2, u_mix);
diff --git a/src/mbgl/shaders/fill_outline.cpp b/src/mbgl/shaders/fill_outline.cpp
index 45bc716be3..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,26 +62,30 @@ 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 = smoothstep(1.0, 0.0, dist);
+ float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
gl_FragColor = outline_color * (alpha * opacity);
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/fill_outline_pattern.cpp b/src/mbgl/shaders/fill_outline_pattern.cpp
index 5315709e3a..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);
@@ -54,6 +56,7 @@ uniform vec2 u_pattern_tl_a;
uniform vec2 u_pattern_br_a;
uniform vec2 u_pattern_tl_b;
uniform vec2 u_pattern_br_b;
+uniform vec2 u_texsize;
uniform float u_mix;
uniform sampler2D u_image;
@@ -69,24 +72,26 @@ 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_pattern_br_a, imagecoord);
+ vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
vec2 imagecoord_b = mod(v_pos_b, 1.0);
- vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b);
+ vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b);
vec4 color2 = texture2D(u_image, pos2);
// find distance to outline for alpha interpolation
float dist = length(v_pos - gl_FragCoord.xy);
- float alpha = smoothstep(1.0, 0.0, dist);
+ float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
gl_FragColor = mix(color1, color2, u_mix) * alpha * opacity;
diff --git a/src/mbgl/shaders/fill_pattern.cpp b/src/mbgl/shaders/fill_pattern.cpp
index dd99e4efff..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);
@@ -50,6 +52,7 @@ uniform vec2 u_pattern_tl_a;
uniform vec2 u_pattern_br_a;
uniform vec2 u_pattern_tl_b;
uniform vec2 u_pattern_br_b;
+uniform vec2 u_texsize;
uniform float u_mix;
uniform sampler2D u_image;
@@ -64,18 +67,20 @@ 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_pattern_br_a, imagecoord);
+ vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
vec2 imagecoord_b = mod(v_pos_b, 1.0);
- vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b);
+ vec2 pos2 = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, imagecoord_b);
vec4 color2 = texture2D(u_image, pos2);
gl_FragColor = mix(color1, color2, u_mix) * opacity;
diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp
index aa59181fb4..c700295a15 100644
--- a/src/mbgl/shaders/line.cpp
+++ b/src/mbgl/shaders/line.cpp
@@ -26,7 +26,6 @@ attribute vec4 a_data;
uniform mat4 u_matrix;
uniform mediump float u_ratio;
-uniform mediump float u_width;
uniform vec2 u_gl_units_to_pixels;
varying vec2 v_normal;
@@ -42,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;
@@ -50,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;
@@ -58,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;
@@ -65,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;
@@ -72,38 +75,59 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
-void main() {
+#ifndef HAS_UNIFORM_u_width
+uniform lowp float a_width_t;
+attribute mediump vec2 a_width;
+#else
+uniform mediump float u_width;
+#endif
+
+
+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;
@@ -117,11 +141,11 @@ void main() {
// 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 width = u_width / 2.0;
+ float halfwidth = width / 2.0;
offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
- float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
+ float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
// Scale the extrusion vector down to a normal and then up by the line width
// of this vertex.
@@ -155,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 d46858aa9e..f8d785ade9 100644
--- a/src/mbgl/shaders/line_pattern.cpp
+++ b/src/mbgl/shaders/line_pattern.cpp
@@ -28,7 +28,6 @@ attribute vec4 a_data;
uniform mat4 u_matrix;
uniform mediump float u_ratio;
-uniform mediump float u_width;
uniform vec2 u_gl_units_to_pixels;
varying vec2 v_normal;
@@ -45,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;
@@ -53,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;
@@ -60,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;
@@ -67,32 +69,52 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
-void main() {
+#ifndef HAS_UNIFORM_u_width
+uniform lowp float a_width_t;
+attribute mediump vec2 a_width;
+#else
+uniform mediump float u_width;
+#endif
+
+
+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;
@@ -107,11 +129,11 @@ void main() {
// 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 width = u_width / 2.0;
+ float halfwidth = width / 2.0;
offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
- float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
+ float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
// Scale the extrusion vector down to a normal and then up by the line width
// of this vertex.
@@ -145,6 +167,7 @@ uniform vec2 u_pattern_tl_a;
uniform vec2 u_pattern_br_a;
uniform vec2 u_pattern_tl_b;
uniform vec2 u_pattern_br_b;
+uniform vec2 u_texsize;
uniform float u_fade;
uniform sampler2D u_image;
@@ -161,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;
@@ -190,8 +217,8 @@ void main() {
float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0);
float y_a = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_a.y);
float y_b = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_b.y);
- vec2 pos_a = mix(u_pattern_tl_a, u_pattern_br_a, vec2(x_a, y_a));
- vec2 pos_b = mix(u_pattern_tl_b, u_pattern_br_b, vec2(x_b, y_b));
+ vec2 pos_a = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, vec2(x_a, y_a));
+ vec2 pos_b = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, vec2(x_b, y_b));
vec4 color = mix(texture2D(u_image, pos_a), texture2D(u_image, pos_b), u_fade);
diff --git a/src/mbgl/shaders/line_sdf.cpp b/src/mbgl/shaders/line_sdf.cpp
index fb6046d8a5..c5d50566e8 100644
--- a/src/mbgl/shaders/line_sdf.cpp
+++ b/src/mbgl/shaders/line_sdf.cpp
@@ -33,7 +33,6 @@ uniform float u_tex_y_a;
uniform vec2 u_patternscale_b;
uniform float u_tex_y_b;
uniform vec2 u_gl_units_to_pixels;
-uniform mediump float u_width;
varying vec2 v_normal;
varying vec2 v_width2;
@@ -50,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;
@@ -58,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;
@@ -66,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;
@@ -73,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;
@@ -80,38 +83,76 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
-void main() {
+#ifndef HAS_UNIFORM_u_width
+uniform lowp float a_width_t;
+attribute mediump vec2 a_width;
+varying mediump float width;
+#else
+uniform mediump float u_width;
+#endif
+
+
+#ifndef HAS_UNIFORM_u_floorwidth
+uniform lowp float a_floorwidth_t;
+attribute lowp vec2 a_floorwidth;
+varying lowp float floorwidth;
+#else
+uniform lowp float u_floorwidth;
+#endif
+
+
+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;
@@ -126,11 +167,11 @@ void main() {
// 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 width = u_width / 2.0;
+ float halfwidth = width / 2.0;
offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
- float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
+ float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
// Scale the extrusion vector down to a normal and then up by the line width
// of this vertex.
@@ -152,8 +193,8 @@ void main() {
float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels);
v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective;
- v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a);
- v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b);
+ v_tex_a = vec2(a_linesofar * u_patternscale_a.x / floorwidth, normal.y * u_patternscale_a.y + u_tex_y_a);
+ v_tex_b = vec2(a_linesofar * u_patternscale_b.x / floorwidth, normal.y * u_patternscale_b.y + u_tex_y_b);
v_width2 = vec2(outset, inset);
}
@@ -178,32 +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
-void main() {
+#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() {
+
#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;
@@ -216,7 +287,7 @@ void main() {
float sdfdist_a = texture2D(u_image, v_tex_a).a;
float sdfdist_b = texture2D(u_image, v_tex_b).a;
float sdfdist = mix(sdfdist_a, sdfdist_b, u_mix);
- alpha *= smoothstep(0.5 - u_sdfgamma, 0.5 + u_sdfgamma, sdfdist);
+ alpha *= smoothstep(0.5 - u_sdfgamma / floorwidth, 0.5 + u_sdfgamma / floorwidth, sdfdist);
gl_FragColor = color * (alpha * opacity);
diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp
index 95fa624e8d..feb185a684 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:
@@ -54,8 +35,8 @@ vec2 unpack_float(const float packedValue) {
}
-// 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/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp
index bc570cf361..1e96194738 100644
--- a/src/mbgl/shaders/symbol_icon.cpp
+++ b/src/mbgl/shaders/symbol_icon.cpp
@@ -7,17 +7,21 @@ 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;
-// 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 highp float u_collision_y_stretch;
#ifndef HAS_UNIFORM_u_opacity
@@ -28,13 +32,13 @@ 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;
@@ -42,64 +46,73 @@ varying vec2 v_tex;
varying vec2 v_fade_tex;
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 vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]);
+ highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI;
+ mediump float a_labelminzoom = angle_labelminzoom[1];
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);
+ // See comments in symbol_sdf.vertex
+ highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch));
+ highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch);
+
+ highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0);
+ highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0);
+ v_fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0);
}
)MBGL_SHADER";
@@ -114,15 +127,17 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
varying vec2 v_tex;
varying vec2 v_fade_tex;
void main() {
-
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity;
gl_FragColor = texture2D(u_texture, v_tex) * alpha;
diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp
index cce6b769a6..a4427f31ab 100644
--- a/src/mbgl/shaders/symbol_sdf.cpp
+++ b/src/mbgl/shaders/symbol_sdf.cpp
@@ -11,6 +11,7 @@ const float PI = 3.141592653589793;
attribute vec4 a_pos_offset;
attribute vec4 a_data;
+attribute vec3 a_projected_pos;
// contents of a_size vary based on the type of property value
// used for {text,icon}-size.
@@ -18,14 +19,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 +34,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 +43,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 +52,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 +61,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,17 +70,18 @@ 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 highp float u_collision_y_stretch;
uniform vec2 u_texsize;
@@ -86,129 +89,124 @@ varying vec4 v_data0;
varying vec2 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 vec2 angle_labelminzoom = unpack_float(a_projected_pos[2]);
+ highp float segment_angle = -angle_labelminzoom[0] / 255.0 * 2.0 * PI;
+ mediump float a_labelminzoom = angle_labelminzoom[1];
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);
+ // incidence_stretch is the ratio of how much y space a label takes up on a tile while drawn perpendicular to the viewport vs
+ // how much space it would take up if it were drawn flat on the tile
+ // Using law of sines, camera_to_anchor/sin(ground_angle) = camera_to_center/sin(incidence_angle)
+ // sin(incidence_angle) = 1/incidence_stretch
+ // Incidence angle 90 -> head on, sin(incidence_angle) = 1, no incidence stretch
+ // Incidence angle 1 -> very oblique, sin(incidence_angle) =~ 0, lots of incidence stretch
+ // ground_angle = u_pitch + PI/2 -> sin(ground_angle) = cos(u_pitch)
+ // This 2D calculation is only exactly correct when gl_Position.x is in the center of the viewport,
+ // but it's a close enough approximation for our purposes
+ highp float incidence_stretch = camera_to_anchor_distance / (u_camera_to_center_distance * cos(u_pitch));
+ // incidence_stretch only applies to the y-axis, but without re-calculating the collision tile, we can't
+ // adjust the size of only one axis. So, we do a crude approximation at placement time to get the aspect ratio
+ // about right, and then do the rest of the adjustment here: there will be some extra padding on the x-axis,
+ // but hopefully not too much.
+ // Never make the adjustment less than 1.0: instead of allowing collisions on the x-axis, be conservative on
+ // the y-axis.
+ highp float collision_adjustment = max(1.0, incidence_stretch / u_collision_y_stretch);
+
+ // Floor to 1/10th zoom to dodge precision issues that can cause partially hidden labels
+ highp float collision_perspective_ratio = 1.0 + 0.5*((camera_to_anchor_distance / u_camera_to_center_distance) - 1.0);
+ highp float perspective_zoom_adjust = floor(log2(collision_perspective_ratio * collision_adjustment) * 10.0);
+ vec2 fade_tex = vec2((a_labelminzoom + perspective_zoom_adjust) / 255.0, 0.0);
v_data0 = vec4(tex.x, tex.y, fade_tex.x, fade_tex.y);
v_data1 = vec2(gamma_scale, size);
@@ -227,30 +225,35 @@ 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;
@@ -260,27 +263,32 @@ varying vec4 v_data0;
varying vec2 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;
diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp
deleted file mode 100644
index fed8451aed..0000000000
--- a/src/mbgl/sprite/sprite_atlas.cpp
+++ /dev/null
@@ -1,340 +0,0 @@
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/sprite/sprite_atlas_worker.hpp>
-#include <mbgl/sprite/sprite_atlas_observer.hpp>
-#include <mbgl/sprite/sprite_parser.hpp>
-#include <mbgl/gl/context.hpp>
-#include <mbgl/util/logging.hpp>
-#include <mbgl/util/platform.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/exception.hpp>
-#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 <cassert>
-#include <cmath>
-#include <algorithm>
-
-namespace mbgl {
-
-static SpriteAtlasObserver nullObserver;
-
-struct SpriteAtlas::Loader {
- Loader(Scheduler& scheduler, SpriteAtlas& spriteAtlas)
- : mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
- worker(scheduler, ActorRef<SpriteAtlas>(spriteAtlas, mailbox)) {
- }
-
- std::shared_ptr<const std::string> image;
- std::shared_ptr<const std::string> json;
- std::unique_ptr<AsyncRequest> jsonRequest;
- std::unique_ptr<AsyncRequest> spriteRequest;
- std::shared_ptr<Mailbox> mailbox;
- Actor<SpriteAtlasWorker> worker;
-};
-
-SpriteAtlasElement::SpriteAtlasElement(Rect<uint16_t> rect_,
- const style::Image& image,
- Size size_, float pixelRatio)
- : pos(std::move(rect_)),
- sdf(image.sdf),
- relativePixelRatio(image.pixelRatio / pixelRatio),
- width(image.getWidth()),
- height(image.getHeight()) {
-
- const float padding = 1;
-
- const float w = image.getWidth() * relativePixelRatio;
- const float h = image.getHeight() * relativePixelRatio;
-
- size = {{ float(image.getWidth()), image.getHeight() }};
- tl = {{ float(pos.x + padding) / size_.width, float(pos.y + padding) / size_.height }};
- br = {{ float(pos.x + padding + w) / size_.width, float(pos.y + padding + h) / size_.height }};
-}
-
-SpriteAtlas::SpriteAtlas(Size size_, float pixelRatio_)
- : size(std::move(size_)),
- pixelRatio(pixelRatio_),
- observer(&nullObserver),
- bin(size.width, size.height),
- dirty(true) {
-}
-
-SpriteAtlas::~SpriteAtlas() = default;
-
-void SpriteAtlas::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) {
- if (url.empty()) {
- // Treat a non-existent sprite as a successfully loaded empty sprite.
- markAsLoaded();
- return;
- }
-
- loader = std::make_unique<Loader>(scheduler, *this);
-
- loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) {
- if (res.error) {
- observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- loader->json = std::make_shared<const std::string>();
- emitSpriteLoadedIfComplete();
- } else {
- // Only trigger a sprite loaded event we got new data.
- loader->json = res.data;
- emitSpriteLoadedIfComplete();
- }
- });
-
- loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) {
- if (res.error) {
- observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- loader->image = std::make_shared<const std::string>();
- emitSpriteLoadedIfComplete();
- } else {
- loader->image = res.data;
- emitSpriteLoadedIfComplete();
- }
- });
-}
-
-void SpriteAtlas::emitSpriteLoadedIfComplete() {
- assert(loader);
-
- if (!loader->image || !loader->json) {
- return;
- }
-
- loader->worker.invoke(&SpriteAtlasWorker::parse, loader->image, loader->json);
- // TODO: delete the loader?
-}
-
-void SpriteAtlas::onParsed(Images&& result) {
- markAsLoaded();
- for (auto& pair : result) {
- addImage(pair.first, std::move(pair.second));
- }
- observer->onSpriteLoaded();
- for (auto requestor : requestors) {
- requestor->onIconsAvailable(buildIconMap());
- }
- requestors.clear();
-}
-
-void SpriteAtlas::onError(std::exception_ptr err) {
- observer->onSpriteError(err);
-}
-
-void SpriteAtlas::setObserver(SpriteAtlasObserver* observer_) {
- observer = observer_;
-}
-
-void SpriteAtlas::dumpDebugLogs() const {
- Log::Info(Event::General, "SpriteAtlas::loaded: %d", loaded);
-}
-
-void SpriteAtlas::addImage(const std::string& id, std::unique_ptr<style::Image> image_) {
- icons.clear();
-
- auto it = entries.find(id);
- if (it == entries.end()) {
- entries.emplace(id, Entry { std::move(image_), {}, {} });
- return;
- }
-
- Entry& entry = it->second;
-
- // There is already a sprite with that name in our store.
- if (entry.image->image.size != image_->image.size) {
- Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", id.c_str());
- return;
- }
-
- entry.image = std::move(image_);
-
- if (entry.iconRect) {
- copy(entry, &Entry::iconRect);
- }
-
- if (entry.patternRect) {
- copy(entry, &Entry::patternRect);
- }
-}
-
-void SpriteAtlas::removeImage(const std::string& id) {
- icons.clear();
-
- auto it = entries.find(id);
- if (it == entries.end()) {
- return;
- }
-
- Entry& entry = it->second;
-
- if (entry.iconRect) {
- bin.release(*entry.iconRect);
- }
-
- if (entry.patternRect) {
- bin.release(*entry.patternRect);
- }
-
- entries.erase(it);
-}
-
-const style::Image* SpriteAtlas::getImage(const std::string& id) const {
- const auto it = entries.find(id);
- if (it != entries.end()) {
- return it->second.image.get();
- }
- if (!entries.empty()) {
- Log::Info(Event::Sprite, "Can't find sprite named '%s'", id.c_str());
- }
- return nullptr;
-}
-
-void SpriteAtlas::getIcons(IconRequestor& requestor) {
- if (isLoaded()) {
- requestor.onIconsAvailable(buildIconMap());
- } else {
- requestors.insert(&requestor);
- }
-}
-
-void SpriteAtlas::removeRequestor(IconRequestor& requestor) {
- requestors.erase(&requestor);
-}
-
-optional<SpriteAtlasElement> SpriteAtlas::getIcon(const std::string& id) {
- return getImage(id, &Entry::iconRect);
-}
-
-optional<SpriteAtlasElement> SpriteAtlas::getPattern(const std::string& id) {
- return getImage(id, &Entry::patternRect);
-}
-
-optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& id,
- optional<Rect<uint16_t>> Entry::*entryRect) {
-
- auto it = entries.find(id);
- if (it == entries.end()) {
- if (!entries.empty()) {
- Log::Info(Event::Sprite, "Can't find sprite named '%s'", id.c_str());
- }
- return {};
- }
-
- Entry& entry = it->second;
-
- if (entry.*entryRect) {
- assert(entry.image.get());
- return SpriteAtlasElement {
- *(entry.*entryRect),
- *entry.image,
- size,
- pixelRatio
- };
- }
-
- const uint16_t pixelWidth = std::ceil(entry.image->image.size.width / pixelRatio);
- const uint16_t pixelHeight = std::ceil(entry.image->image.size.height / pixelRatio);
-
- // Increase to next number divisible by 4, but at least 1.
- // This is so we can scale down the texture coordinates and pack them
- // into 2 bytes rather than 4 bytes.
- const uint16_t packWidth = (pixelWidth + 1) + (4 - (pixelWidth + 1) % 4);
- const uint16_t packHeight = (pixelHeight + 1) + (4 - (pixelHeight + 1) % 4);
-
- // We have to allocate a new area in the bin, and store an empty image in it.
- Rect<uint16_t> rect = bin.allocate(packWidth, packHeight);
- if (rect.w == 0) {
- if (debug::spriteWarnings) {
- Log::Warning(Event::Sprite, "sprite atlas bitmap overflow");
- }
- return {};
- }
-
- entry.*entryRect = rect;
- copy(entry, entryRect);
-
- return SpriteAtlasElement {
- rect,
- *entry.image,
- size,
- pixelRatio
- };
-}
-
-void SpriteAtlas::copy(const Entry& entry, optional<Rect<uint16_t>> Entry::*entryRect) {
- if (!image.valid()) {
- image = PremultipliedImage({ static_cast<uint32_t>(std::ceil(size.width * pixelRatio)),
- static_cast<uint32_t>(std::ceil(size.height * pixelRatio)) });
- image.fill(0);
- }
-
- const PremultipliedImage& src = entry.image->image;
- const Rect<uint16_t>& rect = *(entry.*entryRect);
-
- const uint32_t padding = 1;
- const uint32_t x = (rect.x + padding) * pixelRatio;
- const uint32_t y = (rect.y + padding) * pixelRatio;
- const uint32_t w = src.size.width;
- const uint32_t h = src.size.height;
-
- PremultipliedImage::copy(src, image, { 0, 0 }, { x, y }, { w, h });
-
- if (entryRect == &Entry::patternRect) {
- // Add 1 pixel wrapped padding on each side of the image.
- PremultipliedImage::copy(src, image, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T
- PremultipliedImage::copy(src, image, { 0, 0 }, { x, y + h }, { w, 1 }); // B
- PremultipliedImage::copy(src, image, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L
- PremultipliedImage::copy(src, image, { 0, 0 }, { x + w, y }, { 1, h }); // R
- }
-
- dirty = true;
-}
-
-IconMap SpriteAtlas::buildIconMap() {
- if (icons.empty()) {
- for (const auto& entry : entries) {
- icons.emplace(std::piecewise_construct,
- std::forward_as_tuple(entry.first),
- std::forward_as_tuple(*getIcon(entry.first)));
-
- }
- }
- return icons;
-}
-
-void SpriteAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
- if (!texture) {
- texture = context.createTexture(image, unit);
- } else if (dirty) {
- context.updateTexture(*texture, image, unit);
- }
-
-#if not MBGL_USE_GLES2
-// if (dirty) {
-// platform::showColorDebugImage("Sprite Atlas",
-// reinterpret_cast<const char*>(image.data.get()), size.width,
-// size.height, image.size.width, image.size.height);
-// }
-#endif // MBGL_USE_GLES2
-
- dirty = false;
-}
-
-void SpriteAtlas::bind(bool linear, gl::Context& context, gl::TextureUnit unit) {
- upload(context, unit);
- context.bindTexture(*texture, unit,
- linear ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp
deleted file mode 100644
index c3efeff44b..0000000000
--- a/src/mbgl/sprite/sprite_atlas.hpp
+++ /dev/null
@@ -1,140 +0,0 @@
-#pragma once
-
-#include <mbgl/geometry/binpack.hpp>
-#include <mbgl/gl/texture.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/style/image.hpp>
-
-#include <string>
-#include <map>
-#include <set>
-#include <unordered_map>
-#include <array>
-#include <memory>
-
-namespace mbgl {
-
-class Scheduler;
-class FileSource;
-class SpriteAtlasObserver;
-
-namespace gl {
-class Context;
-} // namespace gl
-
-class SpriteAtlasElement {
-public:
- SpriteAtlasElement(Rect<uint16_t>, const style::Image&, Size size, float pixelRatio);
-
- Rect<uint16_t> pos;
- bool sdf;
-
- float relativePixelRatio;
- std::array<float, 2> size;
- std::array<float, 2> tl;
- std::array<float, 2> br;
- float width;
- float height;
-};
-
-typedef std::map<std::string, SpriteAtlasElement> IconMap;
-typedef std::set<std::string> IconDependencies;
-
-class IconRequestor {
-public:
- virtual ~IconRequestor() = default;
- virtual void onIconsAvailable(IconMap) = 0;
-};
-
-class SpriteAtlas : public util::noncopyable {
-public:
- using Images = std::map<std::string, std::unique_ptr<style::Image>>;
-
- SpriteAtlas(Size, float pixelRatio);
- ~SpriteAtlas();
-
- void load(const std::string& url, Scheduler&, FileSource&);
-
- void markAsLoaded() {
- loaded = true;
- }
-
- bool isLoaded() const {
- return loaded;
- }
-
- void dumpDebugLogs() const;
-
- void setObserver(SpriteAtlasObserver*);
-
- const style::Image* getImage(const std::string&) const;
- void addImage(const std::string&, std::unique_ptr<style::Image>);
- void removeImage(const std::string&);
-
- void getIcons(IconRequestor& requestor);
- void removeRequestor(IconRequestor& requestor);
-
- optional<SpriteAtlasElement> getIcon(const std::string& name);
- optional<SpriteAtlasElement> getPattern(const std::string& name);
-
- // Binds the atlas texture to the GPU, and uploads data if it is out of date.
- void bind(bool linear, gl::Context&, gl::TextureUnit unit);
-
- // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
- // the texture is only bound when the data is out of date (=dirty).
- void upload(gl::Context&, gl::TextureUnit unit);
-
- Size getSize() const { return size; }
- float getPixelRatio() const { return pixelRatio; }
-
- // Only for use in tests.
- const PremultipliedImage& getAtlasImage() const {
- return image;
- }
-
-private:
- void emitSpriteLoadedIfComplete();
-
- // Invoked by SpriteAtlasWorker
- friend class SpriteAtlasWorker;
- void onParsed(Images&& result);
- void onError(std::exception_ptr);
-
- const Size size;
- const float pixelRatio;
-
- struct Loader;
- std::unique_ptr<Loader> loader;
-
- bool loaded = false;
-
- SpriteAtlasObserver* observer = nullptr;
-
- struct Entry {
- std::unique_ptr<style::Image> image;
-
- // One sprite image might be used as both an icon image and a pattern image. If so,
- // it must have two distinct entries in the texture. The one for the icon image has
- // a single pixel transparent border, and the one for the pattern image has a single
- // pixel border wrapped from the opposite side.
- optional<Rect<uint16_t>> iconRect;
- optional<Rect<uint16_t>> patternRect;
- };
-
- optional<SpriteAtlasElement> getImage(const std::string& name, optional<Rect<uint16_t>> Entry::*rect);
- void copy(const Entry&, optional<Rect<uint16_t>> Entry::*rect);
-
- IconMap buildIconMap();
-
- std::unordered_map<std::string, Entry> entries;
- BinPack<uint16_t> bin;
- PremultipliedImage image;
- mbgl::optional<gl::Texture> texture;
- bool dirty;
-
- std::set<IconRequestor*> requestors;
- IconMap icons;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_atlas_observer.hpp b/src/mbgl/sprite/sprite_atlas_observer.hpp
deleted file mode 100644
index 263c95b0e8..0000000000
--- a/src/mbgl/sprite/sprite_atlas_observer.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include <exception>
-
-namespace mbgl {
-
-class SpriteAtlasObserver {
-public:
- virtual ~SpriteAtlasObserver() = default;
-
- virtual void onSpriteLoaded() {}
- virtual void onSpriteError(std::exception_ptr) {}
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp
new file mode 100644
index 0000000000..93d6dfd9ae
--- /dev/null
+++ b/src/mbgl/sprite/sprite_loader.cpp
@@ -0,0 +1,104 @@
+#include <mbgl/sprite/sprite_loader.hpp>
+#include <mbgl/sprite/sprite_loader_worker.hpp>
+#include <mbgl/sprite/sprite_loader_observer.hpp>
+#include <mbgl/sprite/sprite_parser.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/platform.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+static SpriteLoaderObserver nullObserver;
+
+struct SpriteLoader::Loader {
+ Loader(Scheduler& scheduler, SpriteLoader& imageManager)
+ : mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
+ worker(scheduler, ActorRef<SpriteLoader>(imageManager, mailbox)) {
+ }
+
+ std::shared_ptr<const std::string> image;
+ std::shared_ptr<const std::string> json;
+ std::unique_ptr<AsyncRequest> jsonRequest;
+ std::unique_ptr<AsyncRequest> spriteRequest;
+ std::shared_ptr<Mailbox> mailbox;
+ Actor<SpriteLoaderWorker> worker;
+};
+
+SpriteLoader::SpriteLoader(float pixelRatio_)
+ : pixelRatio(pixelRatio_)
+ , observer(&nullObserver) {
+}
+
+SpriteLoader::~SpriteLoader() = default;
+
+void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) {
+ if (url.empty()) {
+ // Treat a non-existent sprite as a successfully loaded empty sprite.
+ observer->onSpriteLoaded({});
+ return;
+ }
+
+ loader = std::make_unique<Loader>(scheduler, *this);
+
+ loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) {
+ if (res.error) {
+ observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ loader->json = std::make_shared<const std::string>();
+ emitSpriteLoadedIfComplete();
+ } else {
+ // Only trigger a sprite loaded event we got new data.
+ loader->json = res.data;
+ emitSpriteLoadedIfComplete();
+ }
+ });
+
+ loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) {
+ if (res.error) {
+ observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ loader->image = std::make_shared<const std::string>();
+ emitSpriteLoadedIfComplete();
+ } else {
+ loader->image = res.data;
+ emitSpriteLoadedIfComplete();
+ }
+ });
+}
+
+void SpriteLoader::emitSpriteLoadedIfComplete() {
+ assert(loader);
+
+ if (!loader->image || !loader->json) {
+ return;
+ }
+
+ loader->worker.invoke(&SpriteLoaderWorker::parse, loader->image, loader->json);
+}
+
+void SpriteLoader::onParsed(std::vector<std::unique_ptr<style::Image>>&& result) {
+ observer->onSpriteLoaded(std::move(result));
+}
+
+void SpriteLoader::onError(std::exception_ptr err) {
+ observer->onSpriteError(err);
+}
+
+void SpriteLoader::setObserver(SpriteLoaderObserver* observer_) {
+ observer = observer_;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_loader.hpp b/src/mbgl/sprite/sprite_loader.hpp
new file mode 100644
index 0000000000..0daf46be9c
--- /dev/null
+++ b/src/mbgl/sprite/sprite_loader.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/style/image.hpp>
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+#include <array>
+#include <memory>
+
+namespace mbgl {
+
+class Scheduler;
+class FileSource;
+class SpriteLoaderObserver;
+
+class SpriteLoader : public util::noncopyable {
+public:
+ SpriteLoader(float pixelRatio);
+ ~SpriteLoader();
+
+ void load(const std::string& url, Scheduler&, FileSource&);
+
+ void setObserver(SpriteLoaderObserver*);
+
+private:
+ void emitSpriteLoadedIfComplete();
+
+ // Invoked by SpriteAtlasWorker
+ friend class SpriteLoaderWorker;
+ void onParsed(std::vector<std::unique_ptr<style::Image>>&&);
+ void onError(std::exception_ptr);
+
+ const float pixelRatio;
+
+ struct Loader;
+ std::unique_ptr<Loader> loader;
+
+ SpriteLoaderObserver* observer = nullptr;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_loader_observer.hpp b/src/mbgl/sprite/sprite_loader_observer.hpp
new file mode 100644
index 0000000000..c730549c2c
--- /dev/null
+++ b/src/mbgl/sprite/sprite_loader_observer.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <exception>
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+
+namespace style {
+class Image;
+} // namespace style
+
+class SpriteLoaderObserver {
+public:
+ virtual ~SpriteLoaderObserver() = default;
+
+ virtual void onSpriteLoaded(std::vector<std::unique_ptr<style::Image>>&&) {}
+ virtual void onSpriteError(std::exception_ptr) {}
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_atlas_worker.cpp b/src/mbgl/sprite/sprite_loader_worker.cpp
index da507aabab..4bded33d53 100644
--- a/src/mbgl/sprite/sprite_atlas_worker.cpp
+++ b/src/mbgl/sprite/sprite_loader_worker.cpp
@@ -1,14 +1,14 @@
-#include <mbgl/sprite/sprite_atlas_worker.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/sprite/sprite_loader_worker.hpp>
+#include <mbgl/sprite/sprite_loader.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
namespace mbgl {
-SpriteAtlasWorker::SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas> parent_)
+SpriteLoaderWorker::SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<SpriteLoader> parent_)
: parent(std::move(parent_)) {
}
-void SpriteAtlasWorker::parse(std::shared_ptr<const std::string> image,
+void SpriteLoaderWorker::parse(std::shared_ptr<const std::string> image,
std::shared_ptr<const std::string> json) {
try {
if (!image) {
@@ -20,9 +20,9 @@ void SpriteAtlasWorker::parse(std::shared_ptr<const std::string> image,
throw std::runtime_error("missing sprite metadata");
}
- parent.invoke(&SpriteAtlas::onParsed, parseSprite(*image, *json));
+ parent.invoke(&SpriteLoader::onParsed, parseSprite(*image, *json));
} catch (...) {
- parent.invoke(&SpriteAtlas::onError, std::current_exception());
+ parent.invoke(&SpriteLoader::onError, std::current_exception());
}
}
diff --git a/src/mbgl/sprite/sprite_atlas_worker.hpp b/src/mbgl/sprite/sprite_loader_worker.hpp
index 70ca0d52e5..d61e07d14f 100644
--- a/src/mbgl/sprite/sprite_atlas_worker.hpp
+++ b/src/mbgl/sprite/sprite_loader_worker.hpp
@@ -8,16 +8,16 @@
namespace mbgl {
-class SpriteAtlas;
+class SpriteLoader;
-class SpriteAtlasWorker {
+class SpriteLoaderWorker {
public:
- SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas>);
+ SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<SpriteLoader>);
void parse(std::shared_ptr<const std::string> image, std::shared_ptr<const std::string> json);
private:
- ActorRef<SpriteAtlas> parent;
+ ActorRef<SpriteLoader> parent;
};
} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp
index c3ed20d03f..1a36e3e990 100644
--- a/src/mbgl/sprite/sprite_parser.cpp
+++ b/src/mbgl/sprite/sprite_parser.cpp
@@ -13,13 +13,14 @@
namespace mbgl {
-std::unique_ptr<style::Image> createStyleImage(const PremultipliedImage& image,
- const uint32_t srcX,
- const uint32_t srcY,
- const uint32_t width,
- const uint32_t height,
- const double ratio,
- const bool sdf) {
+std::unique_ptr<style::Image> createStyleImage(const std::string& id,
+ const PremultipliedImage& image,
+ const uint32_t srcX,
+ const uint32_t srcY,
+ const uint32_t width,
+ const uint32_t height,
+ const double ratio,
+ const bool sdf) {
// Disallow invalid parameter configurations.
if (width <= 0 || height <= 0 || width > 1024 || height > 1024 ||
ratio <= 0 || ratio > 10 ||
@@ -37,7 +38,7 @@ std::unique_ptr<style::Image> createStyleImage(const PremultipliedImage& image,
// Copy from the source image into our individual sprite image
PremultipliedImage::copy(image, dstImage, { srcX, srcY }, { 0, 0 }, { width, height });
- return std::make_unique<style::Image>(std::move(dstImage), ratio, sdf);
+ return std::make_unique<style::Image>(id, std::move(dstImage), ratio, sdf);
}
namespace {
@@ -84,7 +85,7 @@ bool getBoolean(const JSValue& value, const char* name, const bool def = false)
} // namespace
-Images parseSprite(const std::string& encodedImage, const std::string& json) {
+std::vector<std::unique_ptr<style::Image>> parseSprite(const std::string& encodedImage, const std::string& json) {
const PremultipliedImage raster = decodeImage(encodedImage);
JSDocument doc;
@@ -96,7 +97,7 @@ Images parseSprite(const std::string& encodedImage, const std::string& json) {
} else if (!doc.IsObject()) {
throw std::runtime_error("Sprite JSON root must be an object");
} else {
- Images images;
+ std::vector<std::unique_ptr<style::Image>> images;
for (const auto& property : doc.GetObject()) {
const std::string name = { property.name.GetString(), property.name.GetStringLength() };
const JSValue& value = property.value;
@@ -109,9 +110,9 @@ Images parseSprite(const std::string& encodedImage, const std::string& json) {
const double pixelRatio = getDouble(value, "pixelRatio", 1);
const bool sdf = getBoolean(value, "sdf", false);
- auto image = createStyleImage(raster, x, y, width, height, pixelRatio, sdf);
+ auto image = createStyleImage(name, raster, x, y, width, height, pixelRatio, sdf);
if (image) {
- images.emplace(name, std::move(image));
+ images.push_back(std::move(image));
}
}
}
diff --git a/src/mbgl/sprite/sprite_parser.hpp b/src/mbgl/sprite/sprite_parser.hpp
index 5be8435ebb..f602818d3b 100644
--- a/src/mbgl/sprite/sprite_parser.hpp
+++ b/src/mbgl/sprite/sprite_parser.hpp
@@ -1,13 +1,10 @@
#pragma once
#include <mbgl/util/image.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/variant.hpp>
-#include <mbgl/util/geo.hpp>
#include <string>
#include <memory>
-#include <map>
+#include <vector>
namespace mbgl {
@@ -16,17 +13,16 @@ class Image;
} // namespace style
// Extracts an individual image from a spritesheet from the given location.
-std::unique_ptr<style::Image> createStyleImage(const PremultipliedImage&,
- uint32_t srcX,
- uint32_t srcY,
- uint32_t srcWidth,
- uint32_t srcHeight,
- double ratio,
- bool sdf);
-
-using Images = std::map<std::string, std::unique_ptr<style::Image>>;
+std::unique_ptr<style::Image> createStyleImage(const std::string& id,
+ const PremultipliedImage&,
+ uint32_t srcX,
+ uint32_t srcY,
+ uint32_t srcWidth,
+ uint32_t srcHeight,
+ double ratio,
+ bool sdf);
// Parses an image and an associated JSON file and returns the sprite objects.
-Images parseSprite(const std::string& image, const std::string& json);
+std::vector<std::unique_ptr<style::Image>> parseSprite(const std::string& image, const std::string& json);
} // namespace mbgl
diff --git a/src/mbgl/storage/asset_file_source.hpp b/src/mbgl/storage/asset_file_source.hpp
index 71e5bbdab3..6ed7af8aaf 100644
--- a/src/mbgl/storage/asset_file_source.hpp
+++ b/src/mbgl/storage/asset_file_source.hpp
@@ -17,7 +17,8 @@ public:
private:
class Impl;
- std::unique_ptr<util::Thread<Impl>> thread;
+
+ std::unique_ptr<util::Thread<Impl>> impl;
};
} // namespace mbgl
diff --git a/src/mbgl/storage/file_source_request.cpp b/src/mbgl/storage/file_source_request.cpp
new file mode 100644
index 0000000000..09ea8cc32a
--- /dev/null
+++ b/src/mbgl/storage/file_source_request.cpp
@@ -0,0 +1,37 @@
+#include <mbgl/storage/file_source_request.hpp>
+
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+namespace mbgl {
+
+FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback)
+ : responseCallback(callback)
+ , mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())) {
+}
+
+FileSourceRequest::~FileSourceRequest() {
+ if (cancelCallback) {
+ cancelCallback();
+ }
+
+ mailbox->close();
+}
+
+void FileSourceRequest::onCancel(std::function<void()>&& callback) {
+ cancelCallback = std::move(callback);
+}
+
+void FileSourceRequest::setResponse(const Response& response) {
+ // Copy, because calling the callback will sometimes self
+ // destroy this object. We cannot move because this method
+ // can be called more than one.
+ auto callback = responseCallback;
+ callback(response);
+}
+
+ActorRef<FileSourceRequest> FileSourceRequest::actor() {
+ return ActorRef<FileSourceRequest>(*this, mailbox);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/storage/file_source_request.hpp b/src/mbgl/storage/file_source_request.hpp
new file mode 100644
index 0000000000..6bd0d44df6
--- /dev/null
+++ b/src/mbgl/storage/file_source_request.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/async_request.hpp>
+
+#include <memory>
+#include <functional>
+
+namespace mbgl {
+
+class Mailbox;
+
+class FileSourceRequest : public AsyncRequest {
+public:
+ FileSourceRequest(FileSource::Callback&& callback);
+ ~FileSourceRequest() final;
+
+ void onCancel(std::function<void()>&& callback);
+ void setResponse(const Response& res);
+
+ ActorRef<FileSourceRequest> actor();
+
+private:
+ FileSource::Callback responseCallback = nullptr;
+ std::function<void()> cancelCallback = nullptr;
+
+ std::shared_ptr<Mailbox> mailbox;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/storage/local_file_source.hpp b/src/mbgl/storage/local_file_source.hpp
index 43319bc06e..0f065e0b5f 100644
--- a/src/mbgl/storage/local_file_source.hpp
+++ b/src/mbgl/storage/local_file_source.hpp
@@ -19,7 +19,8 @@ public:
private:
class Impl;
- std::unique_ptr<util::Thread<Impl>> thread;
+
+ std::unique_ptr<util::Thread<Impl>> impl;
};
} // namespace mbgl
diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp
index 20dde1db56..94bba7f8bf 100644
--- a/src/mbgl/storage/resource.cpp
+++ b/src/mbgl/storage/resource.cpp
@@ -53,6 +53,13 @@ Resource Resource::source(const std::string& url) {
};
}
+Resource Resource::image(const std::string& url) {
+ return Resource {
+ Resource::Kind::Image,
+ url
+ };
+}
+
Resource Resource::spriteImage(const std::string& base, float pixelRatio) {
return Resource {
Resource::Kind::SpriteImage,
diff --git a/src/mbgl/storage/resource_transform.cpp b/src/mbgl/storage/resource_transform.cpp
new file mode 100644
index 0000000000..a5e62b2c1a
--- /dev/null
+++ b/src/mbgl/storage/resource_transform.cpp
@@ -0,0 +1,13 @@
+#include <mbgl/storage/resource_transform.hpp>
+
+namespace mbgl {
+
+ResourceTransform::ResourceTransform(ActorRef<ResourceTransform>, TransformCallback&& callback)
+ : transformCallback(std::move(callback)) {
+}
+
+void ResourceTransform::transform(Resource::Kind kind, const std::string&& url, FinishedCallback&& finished) {
+ finished(transformCallback(kind, std::move(url)));
+}
+
+} // namespace mbgl
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/class_dictionary.cpp b/src/mbgl/style/class_dictionary.cpp
deleted file mode 100644
index ec06ee7d9d..0000000000
--- a/src/mbgl/style/class_dictionary.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include <mbgl/style/class_dictionary.hpp>
-
-#include <pthread.h>
-
-namespace mbgl {
-namespace style {
-
-ClassDictionary::ClassDictionary() {}
-
-ClassDictionary &ClassDictionary::Get() {
- static pthread_once_t store_once = PTHREAD_ONCE_INIT;
- static pthread_key_t store_key;
-
- // Create the key.
- pthread_once(&store_once, []() {
- pthread_key_create(&store_key, [](void *ptr) {
- delete reinterpret_cast<ClassDictionary *>(ptr);
- });
- });
-
- ClassDictionary *ptr = reinterpret_cast<ClassDictionary *>(pthread_getspecific(store_key));
- if (ptr == nullptr) {
- ptr = new ClassDictionary();
- pthread_setspecific(store_key, ptr);
- }
-
- return *ptr;
-}
-
-ClassID ClassDictionary::lookup(const std::string &class_name) {
- auto it = store.find(class_name);
- if (it == store.end()) {
- // Insert the class name into the store.
- ClassID id = ClassID(uint32_t(ClassID::Named) + offset++);
- store.emplace(class_name, id);
- return id;
- } else {
- return it->second;
- }
-}
-
-ClassID ClassDictionary::normalize(ClassID id) {
- if (id >= ClassID::Named) {
- return ClassID::Named;
- } else {
- return id;
- }
-}
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/class_dictionary.hpp b/src/mbgl/style/class_dictionary.hpp
deleted file mode 100644
index 37eb488240..0000000000
--- a/src/mbgl/style/class_dictionary.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <string>
-#include <unordered_map>
-#include <functional>
-
-namespace mbgl {
-namespace style {
-
-enum class ClassID : uint32_t {
- Default = 1, // These values are from the default style for a layer
- Named = 2 // These values (and all subsequent IDs) are from a named style from the layer
-};
-
-class ClassDictionary {
-private:
- ClassDictionary();
-
-public:
- static ClassDictionary &Get();
-
- // Returns an ID for a class name. If the class name does not yet have an ID, one is
- // auto-generated and stored for future reference.
- ClassID lookup(const std::string &class_name);
-
- // Returns either Fallback, Default or Named, depending on the type of the class id.
- ClassID normalize(ClassID id);
-
-private:
- std::unordered_map<std::string, ClassID> store = { { "", ClassID::Default } };
- uint32_t offset = 0;
-};
-
-} // namespace style
-} // namespace mbgl
-
-namespace std {
-
-// Explicitly define std::hash<style::ClassID> because GCC doesn't automatically use std::hash<> of
-// the underlying enum type.
-
-template <>
-struct hash<mbgl::style::ClassID> {
-public:
- size_t operator()(const mbgl::style::ClassID id) const {
- using T = std::underlying_type_t<mbgl::style::ClassID>;
- return std::hash<T>()(static_cast<T>(id));
- }
-};
-
-} // namespace std
diff --git a/src/mbgl/style/collection.hpp b/src/mbgl/style/collection.hpp
new file mode 100644
index 0000000000..0deb1411b6
--- /dev/null
+++ b/src/mbgl/style/collection.hpp
@@ -0,0 +1,141 @@
+#pragma once
+
+#include <mbgl/util/immutable.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <memory>
+#include <string>
+
+namespace mbgl {
+namespace style {
+
+/*
+ Manages an ordered collection of elements and their `Immutable<Impl>`s. The latter is
+ itself stored in an Immutable container. Using immutability at the collection level
+ allows us to short-circuit significant portions of the RenderStyle update logic via
+ a simple pointer equality check, greatly improving performance.
+
+ Element types are required to have:
+
+ * An `Impl` inner class type
+ * An `Immutable<Impl> baseImpl` member
+ * A `std::string getID() const` method
+*/
+template <class T>
+class Collection {
+public:
+ using Impl = typename T::Impl;
+ using WrapperVector = std::vector<std::unique_ptr<T>>;
+ using ImmutableVector = Immutable<std::vector<Immutable<Impl>>>;
+
+ Collection();
+
+ std::size_t size() const;
+ T* get(const std::string&) const;
+
+ std::vector<T*> getWrappers() const;
+ ImmutableVector getImpls() const { return impls; }
+
+ auto begin() const { return wrappers.begin(); }
+ auto end() const { return wrappers.end(); }
+
+ void clear();
+
+ T* add(std::unique_ptr<T>, const optional<std::string>& = {});
+ std::unique_ptr<T> remove(const std::string&);
+
+ // Must be called whenever an element of the collection is internally mutated.
+ // Typically, each element permits registration of an observer, and the observer
+ // should call this method.
+ void update(const T&);
+
+private:
+ std::size_t index(const std::string&) const;
+
+ WrapperVector wrappers;
+ ImmutableVector impls;
+};
+
+template <class T>
+Collection<T>::Collection()
+ : impls(makeMutable<std::vector<Immutable<Impl>>>()) {
+}
+
+template <class T>
+std::size_t Collection<T>::size() const {
+ return wrappers.size();
+}
+
+template <class T>
+std::size_t Collection<T>::index(const std::string& id) const {
+ return std::find_if(wrappers.begin(), wrappers.end(), [&](const auto& e) {
+ return e->getID() == id;
+ }) - wrappers.begin();
+}
+
+template <class T>
+T* Collection<T>::get(const std::string& id) const {
+ std::size_t i = index(id);
+ return i < size() ? wrappers[i].get() : nullptr;
+}
+
+template <class T>
+std::vector<T*> Collection<T>::getWrappers() const {
+ std::vector<T*> result;
+ result.reserve(wrappers.size());
+
+ for (auto& wrapper : wrappers) {
+ result.push_back(wrapper.get());
+ }
+
+ return result;
+}
+
+template <class T>
+void Collection<T>::clear() {
+ mutate(impls, [&] (auto& impls_) {
+ impls_.clear();
+ });
+
+ wrappers.clear();
+}
+
+template <class T>
+T* Collection<T>::add(std::unique_ptr<T> wrapper, const optional<std::string>& before) {
+ std::size_t i = before ? index(*before) : size();
+
+ mutate(impls, [&] (auto& impls_) {
+ impls_.emplace(impls_.begin() + i, wrapper->baseImpl);
+ });
+
+ return wrappers.emplace(wrappers.begin() + i, std::move(wrapper))->get();
+}
+
+template <class T>
+std::unique_ptr<T> Collection<T>::remove(const std::string& id) {
+ std::size_t i = index(id);
+
+ if (i >= size()) {
+ return nullptr;
+ }
+
+ auto source = std::move(wrappers[i]);
+
+ mutate(impls, [&] (auto& impls_) {
+ impls_.erase(impls_.begin() + i);
+ });
+
+ wrappers.erase(wrappers.begin() + i);
+
+ return source;
+}
+
+template <class T>
+void Collection<T>::update(const T& wrapper) {
+ mutate(impls, [&] (auto& impls_) {
+ impls_.at(this->index(wrapper.getID())) = wrapper.baseImpl;
+ });
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp
index 4afbf198e5..6ae6fede42 100644
--- a/src/mbgl/style/conversion/stringify.hpp
+++ b/src/mbgl/style/conversion/stringify.hpp
@@ -2,7 +2,7 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/property_value.hpp>
-#include <mbgl/style/layout_property.hpp>
+#include <mbgl/style/data_driven_property_value.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/feature.hpp>
@@ -446,13 +446,6 @@ void stringify(Writer& writer, const DataDrivenPropertyValue<T>& value) {
}
}
-template <class Writer, class... Ps>
-void stringify(Writer& writer, const LayoutProperties<Ps...>& ps) {
- writer.StartObject();
- util::ignore({ (stringify<Ps>(writer, ps.unevaluated.template get<Ps>()), 0)... });
- writer.EndObject();
-}
-
} // namespace conversion
} // 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/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp
index dfb34e9dd4..0ac6fda846 100644
--- a/src/mbgl/style/function/identity_stops.cpp
+++ b/src/mbgl/style/function/identity_stops.cpp
@@ -36,25 +36,53 @@ 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 {};
}
- const std::vector<Value>& vector = value.get<std::vector<Value>>();
+ const auto& vector = value.get<std::vector<Value>>();
if (vector.size() != 2 || !numericValue<float>(vector[0]) || !numericValue<float>(vector[1])) {
return {};
}
- return {{{
+ std::array<float, 2> array {{
*numericValue<float>(vector[0]),
*numericValue<float>(vector[1])
- }}};
+ }};
+ return array;
}
} // namespace style
diff --git a/src/mbgl/style/image.cpp b/src/mbgl/style/image.cpp
index 4c0c6a859b..1747de5fcc 100644
--- a/src/mbgl/style/image.cpp
+++ b/src/mbgl/style/image.cpp
@@ -1,21 +1,33 @@
#include <mbgl/style/image.hpp>
+#include <mbgl/style/image_impl.hpp>
#include <mbgl/util/exception.hpp>
namespace mbgl {
namespace style {
-Image::Image(PremultipliedImage&& image_,
- const float pixelRatio_,
- bool sdf_)
- : image(std::move(image_)),
- pixelRatio(pixelRatio_),
- sdf(sdf_) {
-
- if (!image.valid()) {
- throw util::SpriteImageException("Sprite image dimensions may not be zero");
- } else if (pixelRatio <= 0) {
- throw util::SpriteImageException("Sprite pixelRatio may not be <= 0");
- }
+Image::Image(std::string id,
+ PremultipliedImage &&image,
+ const float pixelRatio,
+ bool sdf)
+ : baseImpl(makeMutable<Impl>(std::move(id), std::move(image), pixelRatio, sdf)) {
+}
+
+std::string Image::getID() const {
+ return baseImpl->id;
+}
+
+Image::Image(const Image&) = default;
+
+const PremultipliedImage& Image::getImage() const {
+ return baseImpl->image;
+}
+
+bool Image::isSdf() const {
+ return baseImpl->sdf;
+}
+
+float Image::getPixelRatio() const {
+ return baseImpl->pixelRatio;
}
} // namespace style
diff --git a/src/mbgl/style/image_impl.cpp b/src/mbgl/style/image_impl.cpp
new file mode 100644
index 0000000000..ce327262e8
--- /dev/null
+++ b/src/mbgl/style/image_impl.cpp
@@ -0,0 +1,24 @@
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/util/exception.hpp>
+
+namespace mbgl {
+namespace style {
+
+Image::Impl::Impl(std::string id_,
+ PremultipliedImage&& image_,
+ const float pixelRatio_,
+ bool sdf_)
+ : id(std::move(id_)),
+ image(std::move(image_)),
+ pixelRatio(pixelRatio_),
+ sdf(sdf_) {
+
+ if (!image.valid()) {
+ throw util::SpriteImageException("Sprite image dimensions may not be zero");
+ } else if (pixelRatio <= 0) {
+ throw util::SpriteImageException("Sprite pixelRatio may not be <= 0");
+ }
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp
new file mode 100644
index 0000000000..e439e42695
--- /dev/null
+++ b/src/mbgl/style/image_impl.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <mbgl/style/image.hpp>
+
+#include <string>
+#include <unordered_map>
+#include <set>
+
+namespace mbgl {
+namespace style {
+
+class Image::Impl {
+public:
+ Impl(std::string id, PremultipliedImage&&, float pixelRatio, bool sdf = false);
+
+ const std::string id;
+
+ PremultipliedImage image;
+
+ // Pixel ratio of the sprite image.
+ const float pixelRatio;
+
+ // Whether this image should be interpreted as a signed distance field icon.
+ const bool sdf;
+};
+
+} // namespace style
+
+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/layer.cpp b/src/mbgl/style/layer.cpp
index e2eba0e2e0..142fe313cf 100644
--- a/src/mbgl/style/layer.cpp
+++ b/src/mbgl/style/layer.cpp
@@ -1,17 +1,24 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/layer_impl.hpp>
-#include <mbgl/style/layer_type.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
-Layer::Layer(LayerType type_, std::unique_ptr<Impl> baseImpl_)
- : type(type_), baseImpl(std::move(baseImpl_)) {
+static LayerObserver nullObserver;
+
+Layer::Layer(Immutable<Impl> impl)
+ : baseImpl(std::move(impl)),
+ observer(&nullObserver) {
}
Layer::~Layer() = default;
-const std::string& Layer::getID() const {
+LayerType Layer::getType() const {
+ return baseImpl->type;
+}
+
+std::string Layer::getID() const {
return baseImpl->id;
}
@@ -19,27 +26,16 @@ VisibilityType Layer::getVisibility() const {
return baseImpl->visibility;
}
-void Layer::setVisibility(VisibilityType value) {
- if (value == getVisibility())
- return;
- baseImpl->visibility = value;
- baseImpl->observer->onLayerVisibilityChanged(*this);
-}
-
float Layer::getMinZoom() const {
return baseImpl->minZoom;
}
-void Layer::setMinZoom(float minZoom) const {
- baseImpl->minZoom = minZoom;
-}
-
float Layer::getMaxZoom() const {
return baseImpl->maxZoom;
}
-void Layer::setMaxZoom(float maxZoom) const {
- baseImpl->maxZoom = maxZoom;
+void Layer::setObserver(LayerObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver;
}
} // namespace style
diff --git a/src/mbgl/style/layer_impl.cpp b/src/mbgl/style/layer_impl.cpp
index b8eb01fe77..a9a3941f3e 100644
--- a/src/mbgl/style/layer_impl.cpp
+++ b/src/mbgl/style/layer_impl.cpp
@@ -3,16 +3,10 @@
namespace mbgl {
namespace style {
-std::unique_ptr<Layer> Layer::Impl::copy(const std::string& id_,
- const std::string& source_) const {
- std::unique_ptr<Layer> result = clone();
- result->baseImpl->id = id_;
- result->baseImpl->source = source_;
- return result;
-}
-
-void Layer::Impl::setObserver(LayerObserver* observer_) {
- observer = observer_ ? observer_ : &nullObserver;
+Layer::Impl::Impl(LayerType type_, std::string layerID, std::string sourceID)
+ : type(type_),
+ id(std::move(layerID)),
+ source(std::move(sourceID)) {
}
} // namespace style
diff --git a/src/mbgl/style/layer_impl.hpp b/src/mbgl/style/layer_impl.hpp
index 6aa8fccaf0..f350044925 100644
--- a/src/mbgl/style/layer_impl.hpp
+++ b/src/mbgl/style/layer_impl.hpp
@@ -3,13 +3,10 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/style/filter.hpp>
-#include <mbgl/style/layer_observer.hpp>
-#include <mbgl/util/noncopyable.hpp>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
-#include <memory>
#include <string>
#include <limits>
@@ -32,27 +29,19 @@ namespace style {
*/
class Layer::Impl {
public:
+ Impl(LayerType, std::string layerID, std::string sourceID);
virtual ~Impl() = default;
- // Create a new layer with the specified `id` and `sourceID`. All other properties
- // are copied from this layer.
- std::unique_ptr<Layer> copy(const std::string& id,
- const std::string& sourceID) const;
-
- // Create an identical copy of this layer.
- virtual std::unique_ptr<Layer> clone() const = 0;
+ Impl& operator=(const Impl&) = delete;
- // Create a layer, copying all properties except id and paint properties from this layer.
- virtual std::unique_ptr<Layer> cloneRef(const std::string& id) const = 0;
+ // Returns true buckets if properties affecting layout have changed: i.e. filter,
+ // visibility, layout properties, or data-driven paint properties.
+ virtual bool hasLayoutDifference(const Layer::Impl&) const = 0;
// Utility function for automatic layer grouping.
virtual void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const = 0;
- virtual std::unique_ptr<RenderLayer> createRenderLayer() const = 0;
-
- void setObserver(LayerObserver*);
-
-public:
+ const LayerType type;
std::string id;
std::string source;
std::string sourceLayer;
@@ -61,13 +50,8 @@ public:
float maxZoom = std::numeric_limits<float>::infinity();
VisibilityType visibility = VisibilityType::Visible;
- LayerObserver nullObserver;
- LayerObserver* observer = &nullObserver;
-
protected:
- Impl() = default;
Impl(const Impl&) = default;
- Impl& operator=(const Impl&) = delete;
};
} // namespace style
diff --git a/src/mbgl/style/layer_observer.hpp b/src/mbgl/style/layer_observer.hpp
index 2fa1c39660..28074a65e7 100644
--- a/src/mbgl/style/layer_observer.hpp
+++ b/src/mbgl/style/layer_observer.hpp
@@ -9,11 +9,7 @@ class LayerObserver {
public:
virtual ~LayerObserver() = default;
- virtual void onLayerFilterChanged(Layer&) {}
- virtual void onLayerVisibilityChanged(Layer&) {}
- virtual void onLayerPaintPropertyChanged(Layer&) {}
- virtual void onLayerDataDrivenPaintPropertyChanged(Layer&) {}
- virtual void onLayerLayoutPropertyChanged(Layer&, const char *) {}
+ virtual void onLayerChanged(Layer&) {}
};
} // namespace style
diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp
index b4ffea138b..d4ead18816 100644
--- a/src/mbgl/style/layers/background_layer.cpp
+++ b/src/mbgl/style/layers/background_layer.cpp
@@ -2,39 +2,65 @@
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
BackgroundLayer::BackgroundLayer(const std::string& layerID)
- : Layer(LayerType::Background, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
+ : Layer(makeMutable<Impl>(LayerType::Background, layerID, std::string())) {
}
-BackgroundLayer::BackgroundLayer(const Impl& other)
- : Layer(LayerType::Background, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+BackgroundLayer::BackgroundLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
BackgroundLayer::~BackgroundLayer() = default;
-std::unique_ptr<Layer> BackgroundLayer::Impl::clone() const {
- return std::make_unique<BackgroundLayer>(*this);
+const BackgroundLayer::Impl& BackgroundLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> BackgroundLayer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<BackgroundLayer>(*this);
- result->impl->id = id_;
- result->impl->cascading = BackgroundPaintProperties::Cascading();
- return std::move(result);
+Mutable<BackgroundLayer::Impl> BackgroundLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> BackgroundLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = BackgroundPaintProperties::Transitionable();
+ return std::make_unique<BackgroundLayer>(std::move(impl_));
}
void BackgroundLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
+// Visibility
+
+void BackgroundLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void BackgroundLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void BackgroundLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+}
+
// Layout properties
@@ -44,69 +70,81 @@ PropertyValue<Color> BackgroundLayer::getDefaultBackgroundColor() {
return { Color::black() };
}
-PropertyValue<Color> BackgroundLayer::getBackgroundColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<BackgroundColor>().get(klass);
+PropertyValue<Color> BackgroundLayer::getBackgroundColor() const {
+ return impl().paint.template get<BackgroundColor>().value;
}
-void BackgroundLayer::setBackgroundColor(PropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getBackgroundColor(klass))
+void BackgroundLayer::setBackgroundColor(PropertyValue<Color> value) {
+ if (value == getBackgroundColor())
return;
- impl->cascading.template get<BackgroundColor>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<BackgroundColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void BackgroundLayer::setBackgroundColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<BackgroundColor>().setTransition(value, klass);
+void BackgroundLayer::setBackgroundColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<BackgroundColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions BackgroundLayer::getBackgroundColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<BackgroundColor>().getTransition(klass);
+TransitionOptions BackgroundLayer::getBackgroundColorTransition() const {
+ return impl().paint.template get<BackgroundColor>().options;
}
PropertyValue<std::string> BackgroundLayer::getDefaultBackgroundPattern() {
return { "" };
}
-PropertyValue<std::string> BackgroundLayer::getBackgroundPattern(const optional<std::string>& klass) const {
- return impl->cascading.template get<BackgroundPattern>().get(klass);
+PropertyValue<std::string> BackgroundLayer::getBackgroundPattern() const {
+ return impl().paint.template get<BackgroundPattern>().value;
}
-void BackgroundLayer::setBackgroundPattern(PropertyValue<std::string> value, const optional<std::string>& klass) {
- if (value == getBackgroundPattern(klass))
+void BackgroundLayer::setBackgroundPattern(PropertyValue<std::string> value) {
+ if (value == getBackgroundPattern())
return;
- impl->cascading.template get<BackgroundPattern>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<BackgroundPattern>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void BackgroundLayer::setBackgroundPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<BackgroundPattern>().setTransition(value, klass);
+void BackgroundLayer::setBackgroundPatternTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<BackgroundPattern>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions BackgroundLayer::getBackgroundPatternTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<BackgroundPattern>().getTransition(klass);
+TransitionOptions BackgroundLayer::getBackgroundPatternTransition() const {
+ return impl().paint.template get<BackgroundPattern>().options;
}
PropertyValue<float> BackgroundLayer::getDefaultBackgroundOpacity() {
return { 1 };
}
-PropertyValue<float> BackgroundLayer::getBackgroundOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<BackgroundOpacity>().get(klass);
+PropertyValue<float> BackgroundLayer::getBackgroundOpacity() const {
+ return impl().paint.template get<BackgroundOpacity>().value;
}
-void BackgroundLayer::setBackgroundOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getBackgroundOpacity(klass))
+void BackgroundLayer::setBackgroundOpacity(PropertyValue<float> value) {
+ if (value == getBackgroundOpacity())
return;
- impl->cascading.template get<BackgroundOpacity>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<BackgroundOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void BackgroundLayer::setBackgroundOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<BackgroundOpacity>().setTransition(value, klass);
+void BackgroundLayer::setBackgroundOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<BackgroundOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions BackgroundLayer::getBackgroundOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<BackgroundOpacity>().getTransition(klass);
+TransitionOptions BackgroundLayer::getBackgroundOpacityTransition() const {
+ return impl().paint.template get<BackgroundOpacity>().options;
}
} // namespace style
diff --git a/src/mbgl/style/layers/background_layer_impl.cpp b/src/mbgl/style/layers/background_layer_impl.cpp
index 6c4a4c26d9..a59a84fbe9 100644
--- a/src/mbgl/style/layers/background_layer_impl.cpp
+++ b/src/mbgl/style/layers/background_layer_impl.cpp
@@ -1,11 +1,10 @@
#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/renderer/render_background_layer.hpp>
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> BackgroundLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderBackgroundLayer>(*this);
+bool BackgroundLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
+ return false;
}
} // namespace style
diff --git a/src/mbgl/style/layers/background_layer_impl.hpp b/src/mbgl/style/layers/background_layer_impl.hpp
index 85152da4ec..248a751027 100644
--- a/src/mbgl/style/layers/background_layer_impl.hpp
+++ b/src/mbgl/style/layers/background_layer_impl.hpp
@@ -9,13 +9,12 @@ namespace style {
class BackgroundLayer::Impl : public Layer::Impl {
public:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
- void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+ using Layer::Impl::Impl;
- std::unique_ptr<RenderLayer> createRenderLayer() const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- BackgroundPaintProperties::Cascading cascading;
+ BackgroundPaintProperties::Transitionable paint;
};
} // namespace style
diff --git a/src/mbgl/style/layers/background_layer_properties.hpp b/src/mbgl/style/layers/background_layer_properties.hpp
index fae6c26a4b..3a61392fb4 100644
--- a/src/mbgl/style/layers/background_layer_properties.hpp
+++ b/src/mbgl/style/layers/background_layer_properties.hpp
@@ -5,7 +5,9 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
namespace mbgl {
namespace style {
@@ -22,7 +24,7 @@ struct BackgroundOpacity : PaintProperty<float> {
static float defaultValue() { return 1; }
};
-class BackgroundPaintProperties : public PaintProperties<
+class BackgroundPaintProperties : public Properties<
BackgroundColor,
BackgroundPattern,
BackgroundOpacity
diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp
index 8b3431a9a1..9854932699 100644
--- a/src/mbgl/style/layers/circle_layer.cpp
+++ b/src/mbgl/style/layers/circle_layer.cpp
@@ -2,34 +2,34 @@
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
CircleLayer::CircleLayer(const std::string& layerID, const std::string& sourceID)
- : Layer(LayerType::Circle, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
- impl->source = sourceID;
+ : Layer(makeMutable<Impl>(LayerType::Circle, layerID, sourceID)) {
}
-CircleLayer::CircleLayer(const Impl& other)
- : Layer(LayerType::Circle, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+CircleLayer::CircleLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
CircleLayer::~CircleLayer() = default;
-std::unique_ptr<Layer> CircleLayer::Impl::clone() const {
- return std::make_unique<CircleLayer>(*this);
+const CircleLayer::Impl& CircleLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> CircleLayer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<CircleLayer>(*this);
- result->impl->id = id_;
- result->impl->cascading = CirclePaintProperties::Cascading();
- return std::move(result);
+Mutable<CircleLayer::Impl> CircleLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> CircleLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = CirclePaintProperties::Transitionable();
+ return std::make_unique<CircleLayer>(std::move(impl_));
}
void CircleLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
@@ -38,26 +38,55 @@ void CircleLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffe
// Source
const std::string& CircleLayer::getSourceID() const {
- return impl->source;
+ return impl().source;
}
void CircleLayer::setSourceLayer(const std::string& sourceLayer) {
- impl->sourceLayer = sourceLayer;
+ auto impl_ = mutableImpl();
+ impl_->sourceLayer = sourceLayer;
+ baseImpl = std::move(impl_);
}
const std::string& CircleLayer::getSourceLayer() const {
- return impl->sourceLayer;
+ return impl().sourceLayer;
}
// Filter
void CircleLayer::setFilter(const Filter& filter) {
- impl->filter = filter;
- impl->observer->onLayerFilterChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->filter = filter;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
const Filter& CircleLayer::getFilter() const {
- return impl->filter;
+ return impl().filter;
+}
+
+// Visibility
+
+void CircleLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void CircleLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void CircleLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
}
// Layout properties
@@ -69,258 +98,297 @@ DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleRadius() {
return { 5 };
}
-DataDrivenPropertyValue<float> CircleLayer::getCircleRadius(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleRadius>().get(klass);
+DataDrivenPropertyValue<float> CircleLayer::getCircleRadius() const {
+ return impl().paint.template get<CircleRadius>().value;
}
-void CircleLayer::setCircleRadius(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getCircleRadius(klass))
+void CircleLayer::setCircleRadius(DataDrivenPropertyValue<float> value) {
+ if (value == getCircleRadius())
return;
- impl->cascading.template get<CircleRadius>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleRadius>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleRadiusTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleRadius>().setTransition(value, klass);
+void CircleLayer::setCircleRadiusTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleRadius>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleRadiusTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleRadius>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleRadiusTransition() const {
+ return impl().paint.template get<CircleRadius>().options;
}
DataDrivenPropertyValue<Color> CircleLayer::getDefaultCircleColor() {
return { Color::black() };
}
-DataDrivenPropertyValue<Color> CircleLayer::getCircleColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleColor>().get(klass);
+DataDrivenPropertyValue<Color> CircleLayer::getCircleColor() const {
+ return impl().paint.template get<CircleColor>().value;
}
-void CircleLayer::setCircleColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getCircleColor(klass))
+void CircleLayer::setCircleColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getCircleColor())
return;
- impl->cascading.template get<CircleColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleColor>().setTransition(value, klass);
+void CircleLayer::setCircleColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleColor>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleColorTransition() const {
+ return impl().paint.template get<CircleColor>().options;
}
DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleBlur() {
return { 0 };
}
-DataDrivenPropertyValue<float> CircleLayer::getCircleBlur(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleBlur>().get(klass);
+DataDrivenPropertyValue<float> CircleLayer::getCircleBlur() const {
+ return impl().paint.template get<CircleBlur>().value;
}
-void CircleLayer::setCircleBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getCircleBlur(klass))
+void CircleLayer::setCircleBlur(DataDrivenPropertyValue<float> value) {
+ if (value == getCircleBlur())
return;
- impl->cascading.template get<CircleBlur>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleBlur>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleBlur>().setTransition(value, klass);
+void CircleLayer::setCircleBlurTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleBlur>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleBlurTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleBlur>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleBlurTransition() const {
+ return impl().paint.template get<CircleBlur>().options;
}
DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleOpacity() {
return { 1 };
}
-DataDrivenPropertyValue<float> CircleLayer::getCircleOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleOpacity>().get(klass);
+DataDrivenPropertyValue<float> CircleLayer::getCircleOpacity() const {
+ return impl().paint.template get<CircleOpacity>().value;
}
-void CircleLayer::setCircleOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getCircleOpacity(klass))
+void CircleLayer::setCircleOpacity(DataDrivenPropertyValue<float> value) {
+ if (value == getCircleOpacity())
return;
- impl->cascading.template get<CircleOpacity>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleOpacity>().setTransition(value, klass);
+void CircleLayer::setCircleOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleOpacity>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleOpacityTransition() const {
+ return impl().paint.template get<CircleOpacity>().options;
}
PropertyValue<std::array<float, 2>> CircleLayer::getDefaultCircleTranslate() {
return { {{ 0, 0 }} };
}
-PropertyValue<std::array<float, 2>> CircleLayer::getCircleTranslate(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleTranslate>().get(klass);
+PropertyValue<std::array<float, 2>> CircleLayer::getCircleTranslate() const {
+ return impl().paint.template get<CircleTranslate>().value;
}
-void CircleLayer::setCircleTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
- if (value == getCircleTranslate(klass))
+void CircleLayer::setCircleTranslate(PropertyValue<std::array<float, 2>> value) {
+ if (value == getCircleTranslate())
return;
- impl->cascading.template get<CircleTranslate>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleTranslate>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleTranslate>().setTransition(value, klass);
+void CircleLayer::setCircleTranslateTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleTranslate>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleTranslateTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleTranslate>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleTranslateTransition() const {
+ return impl().paint.template get<CircleTranslate>().options;
}
PropertyValue<TranslateAnchorType> CircleLayer::getDefaultCircleTranslateAnchor() {
return { TranslateAnchorType::Map };
}
-PropertyValue<TranslateAnchorType> CircleLayer::getCircleTranslateAnchor(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleTranslateAnchor>().get(klass);
+PropertyValue<TranslateAnchorType> CircleLayer::getCircleTranslateAnchor() const {
+ return impl().paint.template get<CircleTranslateAnchor>().value;
}
-void CircleLayer::setCircleTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
- if (value == getCircleTranslateAnchor(klass))
+void CircleLayer::setCircleTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
+ if (value == getCircleTranslateAnchor())
return;
- impl->cascading.template get<CircleTranslateAnchor>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleTranslateAnchor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleTranslateAnchor>().setTransition(value, klass);
+void CircleLayer::setCircleTranslateAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleTranslateAnchor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleTranslateAnchorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleTranslateAnchor>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleTranslateAnchorTransition() const {
+ return impl().paint.template get<CircleTranslateAnchor>().options;
}
PropertyValue<CirclePitchScaleType> CircleLayer::getDefaultCirclePitchScale() {
return { CirclePitchScaleType::Map };
}
-PropertyValue<CirclePitchScaleType> CircleLayer::getCirclePitchScale(const optional<std::string>& klass) const {
- return impl->cascading.template get<CirclePitchScale>().get(klass);
+PropertyValue<CirclePitchScaleType> CircleLayer::getCirclePitchScale() const {
+ return impl().paint.template get<CirclePitchScale>().value;
+}
+
+void CircleLayer::setCirclePitchScale(PropertyValue<CirclePitchScaleType> value) {
+ if (value == getCirclePitchScale())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchScale>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void CircleLayer::setCirclePitchScaleTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchScale>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+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::setCirclePitchScale(PropertyValue<CirclePitchScaleType> value, const optional<std::string>& klass) {
- if (value == getCirclePitchScale(klass))
+void CircleLayer::setCirclePitchAlignment(PropertyValue<AlignmentType> value) {
+ if (value == getCirclePitchAlignment())
return;
- impl->cascading.template get<CirclePitchScale>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchAlignment>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCirclePitchScaleTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CirclePitchScale>().setTransition(value, klass);
+void CircleLayer::setCirclePitchAlignmentTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchAlignment>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCirclePitchScaleTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CirclePitchScale>().getTransition(klass);
+TransitionOptions CircleLayer::getCirclePitchAlignmentTransition() const {
+ return impl().paint.template get<CirclePitchAlignment>().options;
}
DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeWidth() {
return { 0 };
}
-DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeWidth(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleStrokeWidth>().get(klass);
+DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeWidth() const {
+ return impl().paint.template get<CircleStrokeWidth>().value;
}
-void CircleLayer::setCircleStrokeWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getCircleStrokeWidth(klass))
+void CircleLayer::setCircleStrokeWidth(DataDrivenPropertyValue<float> value) {
+ if (value == getCircleStrokeWidth())
return;
- impl->cascading.template get<CircleStrokeWidth>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleStrokeWidth>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleStrokeWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleStrokeWidth>().setTransition(value, klass);
+void CircleLayer::setCircleStrokeWidthTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleStrokeWidth>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleStrokeWidthTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleStrokeWidth>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleStrokeWidthTransition() const {
+ return impl().paint.template get<CircleStrokeWidth>().options;
}
DataDrivenPropertyValue<Color> CircleLayer::getDefaultCircleStrokeColor() {
return { Color::black() };
}
-DataDrivenPropertyValue<Color> CircleLayer::getCircleStrokeColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleStrokeColor>().get(klass);
+DataDrivenPropertyValue<Color> CircleLayer::getCircleStrokeColor() const {
+ return impl().paint.template get<CircleStrokeColor>().value;
}
-void CircleLayer::setCircleStrokeColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getCircleStrokeColor(klass))
+void CircleLayer::setCircleStrokeColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getCircleStrokeColor())
return;
- impl->cascading.template get<CircleStrokeColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleStrokeColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleStrokeColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleStrokeColor>().setTransition(value, klass);
+void CircleLayer::setCircleStrokeColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleStrokeColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleStrokeColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleStrokeColor>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleStrokeColorTransition() const {
+ return impl().paint.template get<CircleStrokeColor>().options;
}
DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeOpacity() {
return { 1 };
}
-DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleStrokeOpacity>().get(klass);
+DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeOpacity() const {
+ return impl().paint.template get<CircleStrokeOpacity>().value;
}
-void CircleLayer::setCircleStrokeOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getCircleStrokeOpacity(klass))
+void CircleLayer::setCircleStrokeOpacity(DataDrivenPropertyValue<float> value) {
+ if (value == getCircleStrokeOpacity())
return;
- impl->cascading.template get<CircleStrokeOpacity>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleStrokeOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void CircleLayer::setCircleStrokeOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<CircleStrokeOpacity>().setTransition(value, klass);
+void CircleLayer::setCircleStrokeOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CircleStrokeOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions CircleLayer::getCircleStrokeOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<CircleStrokeOpacity>().getTransition(klass);
+TransitionOptions CircleLayer::getCircleStrokeOpacityTransition() const {
+ return impl().paint.template get<CircleStrokeOpacity>().options;
}
} // namespace style
diff --git a/src/mbgl/style/layers/circle_layer_impl.cpp b/src/mbgl/style/layers/circle_layer_impl.cpp
index 31b286f273..69f574cd6b 100644
--- a/src/mbgl/style/layers/circle_layer_impl.cpp
+++ b/src/mbgl/style/layers/circle_layer_impl.cpp
@@ -1,11 +1,14 @@
#include <mbgl/style/layers/circle_layer_impl.hpp>
-#include <mbgl/renderer/render_circle_layer.hpp>
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> CircleLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderCircleLayer>(*this);
+bool CircleLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
+ assert(dynamic_cast<const CircleLayer::Impl*>(&other));
+ const auto& impl = static_cast<const style::CircleLayer::Impl&>(other);
+ return filter != impl.filter ||
+ visibility != impl.visibility ||
+ paint.hasDataDrivenPropertyDifference(impl.paint);
}
} // namespace style
diff --git a/src/mbgl/style/layers/circle_layer_impl.hpp b/src/mbgl/style/layers/circle_layer_impl.hpp
index 886815f0d1..4b148cdc42 100644
--- a/src/mbgl/style/layers/circle_layer_impl.hpp
+++ b/src/mbgl/style/layers/circle_layer_impl.hpp
@@ -9,13 +9,12 @@ namespace style {
class CircleLayer::Impl : public Layer::Impl {
public:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
- void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+ using Layer::Impl::Impl;
- std::unique_ptr<RenderLayer> createRenderLayer() const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- CirclePaintProperties::Cascading cascading;
+ CirclePaintProperties::Transitionable paint;
};
} // namespace style
diff --git a/src/mbgl/style/layers/circle_layer_properties.hpp b/src/mbgl/style/layers/circle_layer_properties.hpp
index 58206c61da..bc0c961e75 100644
--- a/src/mbgl/style/layers/circle_layer_properties.hpp
+++ b/src/mbgl/style/layers/circle_layer_properties.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
@@ -39,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; }
};
@@ -51,7 +56,7 @@ struct CircleStrokeOpacity : DataDrivenPaintProperty<float, attributes::a_stroke
static float defaultValue() { return 1; }
};
-class CirclePaintProperties : public PaintProperties<
+class CirclePaintProperties : public Properties<
CircleRadius,
CircleColor,
CircleBlur,
@@ -59,6 +64,7 @@ class CirclePaintProperties : public PaintProperties<
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 cda8a157f0..854c771847 100644
--- a/src/mbgl/style/layers/custom_layer.cpp
+++ b/src/mbgl/style/layers/custom_layer.cpp
@@ -1,6 +1,6 @@
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/style/layers/custom_layer_impl.hpp>
-#include <mbgl/util/logging.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
@@ -8,23 +8,63 @@ namespace style {
CustomLayer::CustomLayer(const std::string& layerID,
CustomLayerInitializeFunction init,
CustomLayerRenderFunction render,
+ CustomLayerContextLostFunction contextLost,
CustomLayerDeinitializeFunction deinit,
void* context)
- : Layer(LayerType::Custom, std::make_unique<Impl>(layerID, init, render, deinit, context))
- , impl(static_cast<Impl*>(baseImpl.get())) {
- Log::Info(Event::General, "New custom layer: %s", layerID.c_str());
+ : Layer(makeMutable<Impl>(layerID, init, render, contextLost, deinit, context)) {
}
-CustomLayer::CustomLayer(const Impl& other)
- : Layer(LayerType::Custom, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+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;
+const CustomLayer::Impl& CustomLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
+}
+
+Mutable<CustomLayer::Impl> CustomLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> CustomLayer::cloneRef(const std::string&) const {
+ assert(false);
+ return nullptr;
+}
+
+// Visibility
+
+void CustomLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void CustomLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void CustomLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+}
+
template <>
bool Layer::is<CustomLayer>() const {
- return type == LayerType::Custom;
+ return getType() == LayerType::Custom;
}
} // namespace style
diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp
index 1d3e9af8d6..1de268d2e2 100644
--- a/src/mbgl/style/layers/custom_layer_impl.cpp
+++ b/src/mbgl/style/layers/custom_layer_impl.cpp
@@ -1,74 +1,28 @@
#include <mbgl/style/layers/custom_layer_impl.hpp>
-#include <mbgl/renderer/render_custom_layer.hpp>
-#include <mbgl/map/transform_state.hpp>
-#include <mbgl/util/logging.hpp>
+
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> CustomLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderCustomLayer>(*this);
-}
-
CustomLayer::Impl::Impl(const std::string& id_,
CustomLayerInitializeFunction initializeFn_,
CustomLayerRenderFunction renderFn_,
+ CustomLayerContextLostFunction contextLostFn_,
CustomLayerDeinitializeFunction deinitializeFn_,
- void* context_) {
- Log::Info(Event::General, "New custom layer Impl: %s", id_.c_str());
- id = id_;
+ void* context_)
+ : Layer::Impl(LayerType::Custom, id_, std::string()) {
initializeFn = initializeFn_;
renderFn = renderFn_;
deinitializeFn = deinitializeFn_;
+ contextLostFn = contextLostFn_;
context = context_;
}
-CustomLayer::Impl::Impl(const CustomLayer::Impl &other)
- : Layer::Impl(other) {
- id = other.id;
- // Don't copy anything else.
-}
-
-CustomLayer::Impl::~Impl() = default;
-
-std::unique_ptr<Layer> CustomLayer::Impl::clone() const {
- return std::make_unique<CustomLayer>(*this);
-}
-
-std::unique_ptr<Layer> CustomLayer::Impl::cloneRef(const std::string&) const {
- assert(false);
- return std::make_unique<CustomLayer>(*this);
+bool CustomLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
+ return false;
}
void CustomLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
}
-void CustomLayer::Impl::initialize() {
- assert(initializeFn);
- initializeFn(context);
-}
-
-void CustomLayer::Impl::deinitialize() {
- if (deinitializeFn) {
- deinitializeFn(context);
- }
-}
-
-void CustomLayer::Impl::render(const TransformState& state) const {
- assert(renderFn);
-
- CustomLayerRenderParameters parameters;
-
- parameters.width = state.getSize().width;
- parameters.height = state.getSize().height;
- parameters.latitude = state.getLatLng().latitude();
- parameters.longitude = state.getLatLng().longitude();
- parameters.zoom = state.getZoom();
- parameters.bearing = -state.getAngle() * util::RAD2DEG;
- parameters.pitch = state.getPitch();
- parameters.fieldOfView = state.getFieldOfView();
-
- renderFn(context, parameters);
-}
-
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp
index e612d17f14..62efbbe15b 100644
--- a/src/mbgl/style/layers/custom_layer_impl.hpp
+++ b/src/mbgl/style/layers/custom_layer_impl.hpp
@@ -14,25 +14,16 @@ public:
Impl(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
+ CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);
- Impl(const Impl&);
- ~Impl() final;
-
- void initialize();
- void deinitialize();
- void render(const TransformState&) const;
-
-private:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- std::unique_ptr<RenderLayer> createRenderLayer() const final;
-
CustomLayerInitializeFunction initializeFn = nullptr;
CustomLayerRenderFunction renderFn = nullptr;
+ CustomLayerContextLostFunction contextLostFn = nullptr;
CustomLayerDeinitializeFunction deinitializeFn = nullptr;
void* context = nullptr;
};
diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp
index 6f11d6052c..62f92cef75 100644
--- a/src/mbgl/style/layers/fill_extrusion_layer.cpp
+++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp
@@ -2,34 +2,34 @@
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
FillExtrusionLayer::FillExtrusionLayer(const std::string& layerID, const std::string& sourceID)
- : Layer(LayerType::FillExtrusion, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
- impl->source = sourceID;
+ : Layer(makeMutable<Impl>(LayerType::FillExtrusion, layerID, sourceID)) {
}
-FillExtrusionLayer::FillExtrusionLayer(const Impl& other)
- : Layer(LayerType::FillExtrusion, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+FillExtrusionLayer::FillExtrusionLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
FillExtrusionLayer::~FillExtrusionLayer() = default;
-std::unique_ptr<Layer> FillExtrusionLayer::Impl::clone() const {
- return std::make_unique<FillExtrusionLayer>(*this);
+const FillExtrusionLayer::Impl& FillExtrusionLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> FillExtrusionLayer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<FillExtrusionLayer>(*this);
- result->impl->id = id_;
- result->impl->cascading = FillExtrusionPaintProperties::Cascading();
- return std::move(result);
+Mutable<FillExtrusionLayer::Impl> FillExtrusionLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> FillExtrusionLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = FillExtrusionPaintProperties::Transitionable();
+ return std::make_unique<FillExtrusionLayer>(std::move(impl_));
}
void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
@@ -38,26 +38,55 @@ void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::Stri
// Source
const std::string& FillExtrusionLayer::getSourceID() const {
- return impl->source;
+ return impl().source;
}
void FillExtrusionLayer::setSourceLayer(const std::string& sourceLayer) {
- impl->sourceLayer = sourceLayer;
+ auto impl_ = mutableImpl();
+ impl_->sourceLayer = sourceLayer;
+ baseImpl = std::move(impl_);
}
const std::string& FillExtrusionLayer::getSourceLayer() const {
- return impl->sourceLayer;
+ return impl().sourceLayer;
}
// Filter
void FillExtrusionLayer::setFilter(const Filter& filter) {
- impl->filter = filter;
- impl->observer->onLayerFilterChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->filter = filter;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
const Filter& FillExtrusionLayer::getFilter() const {
- return impl->filter;
+ return impl().filter;
+}
+
+// Visibility
+
+void FillExtrusionLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void FillExtrusionLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void FillExtrusionLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
}
// Layout properties
@@ -69,173 +98,189 @@ PropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionOpacity() {
return { 1 };
}
-PropertyValue<float> FillExtrusionLayer::getFillExtrusionOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionOpacity>().get(klass);
+PropertyValue<float> FillExtrusionLayer::getFillExtrusionOpacity() const {
+ return impl().paint.template get<FillExtrusionOpacity>().value;
}
-void FillExtrusionLayer::setFillExtrusionOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getFillExtrusionOpacity(klass))
+void FillExtrusionLayer::setFillExtrusionOpacity(PropertyValue<float> value) {
+ if (value == getFillExtrusionOpacity())
return;
- impl->cascading.template get<FillExtrusionOpacity>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillExtrusionLayer::setFillExtrusionOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillExtrusionOpacity>().setTransition(value, klass);
+void FillExtrusionLayer::setFillExtrusionOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillExtrusionLayer::getFillExtrusionOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionOpacity>().getTransition(klass);
+TransitionOptions FillExtrusionLayer::getFillExtrusionOpacityTransition() const {
+ return impl().paint.template get<FillExtrusionOpacity>().options;
}
DataDrivenPropertyValue<Color> FillExtrusionLayer::getDefaultFillExtrusionColor() {
return { Color::black() };
}
-DataDrivenPropertyValue<Color> FillExtrusionLayer::getFillExtrusionColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionColor>().get(klass);
+DataDrivenPropertyValue<Color> FillExtrusionLayer::getFillExtrusionColor() const {
+ return impl().paint.template get<FillExtrusionColor>().value;
}
-void FillExtrusionLayer::setFillExtrusionColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getFillExtrusionColor(klass))
+void FillExtrusionLayer::setFillExtrusionColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getFillExtrusionColor())
return;
- impl->cascading.template get<FillExtrusionColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillExtrusionLayer::setFillExtrusionColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillExtrusionColor>().setTransition(value, klass);
+void FillExtrusionLayer::setFillExtrusionColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillExtrusionLayer::getFillExtrusionColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionColor>().getTransition(klass);
+TransitionOptions FillExtrusionLayer::getFillExtrusionColorTransition() const {
+ return impl().paint.template get<FillExtrusionColor>().options;
}
PropertyValue<std::array<float, 2>> FillExtrusionLayer::getDefaultFillExtrusionTranslate() {
return { {{ 0, 0 }} };
}
-PropertyValue<std::array<float, 2>> FillExtrusionLayer::getFillExtrusionTranslate(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionTranslate>().get(klass);
+PropertyValue<std::array<float, 2>> FillExtrusionLayer::getFillExtrusionTranslate() const {
+ return impl().paint.template get<FillExtrusionTranslate>().value;
}
-void FillExtrusionLayer::setFillExtrusionTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
- if (value == getFillExtrusionTranslate(klass))
+void FillExtrusionLayer::setFillExtrusionTranslate(PropertyValue<std::array<float, 2>> value) {
+ if (value == getFillExtrusionTranslate())
return;
- impl->cascading.template get<FillExtrusionTranslate>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionTranslate>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillExtrusionLayer::setFillExtrusionTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillExtrusionTranslate>().setTransition(value, klass);
+void FillExtrusionLayer::setFillExtrusionTranslateTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionTranslate>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionTranslate>().getTransition(klass);
+TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateTransition() const {
+ return impl().paint.template get<FillExtrusionTranslate>().options;
}
PropertyValue<TranslateAnchorType> FillExtrusionLayer::getDefaultFillExtrusionTranslateAnchor() {
return { TranslateAnchorType::Map };
}
-PropertyValue<TranslateAnchorType> FillExtrusionLayer::getFillExtrusionTranslateAnchor(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionTranslateAnchor>().get(klass);
+PropertyValue<TranslateAnchorType> FillExtrusionLayer::getFillExtrusionTranslateAnchor() const {
+ return impl().paint.template get<FillExtrusionTranslateAnchor>().value;
}
-void FillExtrusionLayer::setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
- if (value == getFillExtrusionTranslateAnchor(klass))
+void FillExtrusionLayer::setFillExtrusionTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
+ if (value == getFillExtrusionTranslateAnchor())
return;
- impl->cascading.template get<FillExtrusionTranslateAnchor>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionTranslateAnchor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillExtrusionTranslateAnchor>().setTransition(value, klass);
+void FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionTranslateAnchor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateAnchorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionTranslateAnchor>().getTransition(klass);
+TransitionOptions FillExtrusionLayer::getFillExtrusionTranslateAnchorTransition() const {
+ return impl().paint.template get<FillExtrusionTranslateAnchor>().options;
}
PropertyValue<std::string> FillExtrusionLayer::getDefaultFillExtrusionPattern() {
return { "" };
}
-PropertyValue<std::string> FillExtrusionLayer::getFillExtrusionPattern(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionPattern>().get(klass);
+PropertyValue<std::string> FillExtrusionLayer::getFillExtrusionPattern() const {
+ return impl().paint.template get<FillExtrusionPattern>().value;
}
-void FillExtrusionLayer::setFillExtrusionPattern(PropertyValue<std::string> value, const optional<std::string>& klass) {
- if (value == getFillExtrusionPattern(klass))
+void FillExtrusionLayer::setFillExtrusionPattern(PropertyValue<std::string> value) {
+ if (value == getFillExtrusionPattern())
return;
- impl->cascading.template get<FillExtrusionPattern>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionPattern>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillExtrusionLayer::setFillExtrusionPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillExtrusionPattern>().setTransition(value, klass);
+void FillExtrusionLayer::setFillExtrusionPatternTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionPattern>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillExtrusionLayer::getFillExtrusionPatternTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionPattern>().getTransition(klass);
+TransitionOptions FillExtrusionLayer::getFillExtrusionPatternTransition() const {
+ return impl().paint.template get<FillExtrusionPattern>().options;
}
DataDrivenPropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionHeight() {
return { 0 };
}
-DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionHeight(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionHeight>().get(klass);
+DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionHeight() const {
+ return impl().paint.template get<FillExtrusionHeight>().value;
}
-void FillExtrusionLayer::setFillExtrusionHeight(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getFillExtrusionHeight(klass))
+void FillExtrusionLayer::setFillExtrusionHeight(DataDrivenPropertyValue<float> value) {
+ if (value == getFillExtrusionHeight())
return;
- impl->cascading.template get<FillExtrusionHeight>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionHeight>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillExtrusionLayer::setFillExtrusionHeightTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillExtrusionHeight>().setTransition(value, klass);
+void FillExtrusionLayer::setFillExtrusionHeightTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionHeight>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillExtrusionLayer::getFillExtrusionHeightTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionHeight>().getTransition(klass);
+TransitionOptions FillExtrusionLayer::getFillExtrusionHeightTransition() const {
+ return impl().paint.template get<FillExtrusionHeight>().options;
}
DataDrivenPropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionBase() {
return { 0 };
}
-DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionBase(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionBase>().get(klass);
+DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionBase() const {
+ return impl().paint.template get<FillExtrusionBase>().value;
}
-void FillExtrusionLayer::setFillExtrusionBase(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getFillExtrusionBase(klass))
+void FillExtrusionLayer::setFillExtrusionBase(DataDrivenPropertyValue<float> value) {
+ if (value == getFillExtrusionBase())
return;
- impl->cascading.template get<FillExtrusionBase>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionBase>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillExtrusionLayer::setFillExtrusionBaseTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillExtrusionBase>().setTransition(value, klass);
+void FillExtrusionLayer::setFillExtrusionBaseTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillExtrusionBase>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillExtrusionLayer::getFillExtrusionBaseTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillExtrusionBase>().getTransition(klass);
+TransitionOptions FillExtrusionLayer::getFillExtrusionBaseTransition() const {
+ return impl().paint.template get<FillExtrusionBase>().options;
}
} // namespace style
diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp
index 5340541221..d37c2ad29b 100644
--- a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp
+++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp
@@ -1,11 +1,14 @@
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
-#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> FillExtrusionLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderFillExtrusionLayer>(*this);
+bool FillExtrusionLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
+ assert(dynamic_cast<const FillExtrusionLayer::Impl*>(&other));
+ const auto& impl = static_cast<const style::FillExtrusionLayer::Impl&>(other);
+ return filter != impl.filter ||
+ visibility != impl.visibility ||
+ paint.hasDataDrivenPropertyDifference(impl.paint);
}
} // namespace style
diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp
index 2353bd99fe..9abc6fc4b3 100644
--- a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp
+++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp
@@ -9,13 +9,12 @@ namespace style {
class FillExtrusionLayer::Impl : public Layer::Impl {
public:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
- void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+ using Layer::Impl::Impl;
- std::unique_ptr<RenderLayer> createRenderLayer() const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- FillExtrusionPaintProperties::Cascading cascading;
+ FillExtrusionPaintProperties::Transitionable paint;
};
} // namespace style
diff --git a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp
index f41ce68b94..19be59a2fe 100644
--- a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp
+++ b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
@@ -39,7 +40,7 @@ struct FillExtrusionBase : DataDrivenPaintProperty<float, attributes::a_base, un
static float defaultValue() { return 0; }
};
-class FillExtrusionPaintProperties : public PaintProperties<
+class FillExtrusionPaintProperties : public Properties<
FillExtrusionOpacity,
FillExtrusionColor,
FillExtrusionTranslate,
diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp
index 9fd9d33af3..65975752db 100644
--- a/src/mbgl/style/layers/fill_layer.cpp
+++ b/src/mbgl/style/layers/fill_layer.cpp
@@ -2,34 +2,34 @@
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
FillLayer::FillLayer(const std::string& layerID, const std::string& sourceID)
- : Layer(LayerType::Fill, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
- impl->source = sourceID;
+ : Layer(makeMutable<Impl>(LayerType::Fill, layerID, sourceID)) {
}
-FillLayer::FillLayer(const Impl& other)
- : Layer(LayerType::Fill, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+FillLayer::FillLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
FillLayer::~FillLayer() = default;
-std::unique_ptr<Layer> FillLayer::Impl::clone() const {
- return std::make_unique<FillLayer>(*this);
+const FillLayer::Impl& FillLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> FillLayer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<FillLayer>(*this);
- result->impl->id = id_;
- result->impl->cascading = FillPaintProperties::Cascading();
- return std::move(result);
+Mutable<FillLayer::Impl> FillLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> FillLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = FillPaintProperties::Transitionable();
+ return std::make_unique<FillLayer>(std::move(impl_));
}
void FillLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
@@ -38,26 +38,55 @@ void FillLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>
// Source
const std::string& FillLayer::getSourceID() const {
- return impl->source;
+ return impl().source;
}
void FillLayer::setSourceLayer(const std::string& sourceLayer) {
- impl->sourceLayer = sourceLayer;
+ auto impl_ = mutableImpl();
+ impl_->sourceLayer = sourceLayer;
+ baseImpl = std::move(impl_);
}
const std::string& FillLayer::getSourceLayer() const {
- return impl->sourceLayer;
+ return impl().sourceLayer;
}
// Filter
void FillLayer::setFilter(const Filter& filter) {
- impl->filter = filter;
- impl->observer->onLayerFilterChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->filter = filter;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
const Filter& FillLayer::getFilter() const {
- return impl->filter;
+ return impl().filter;
+}
+
+// Visibility
+
+void FillLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void FillLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void FillLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
}
// Layout properties
@@ -69,173 +98,189 @@ PropertyValue<bool> FillLayer::getDefaultFillAntialias() {
return { true };
}
-PropertyValue<bool> FillLayer::getFillAntialias(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillAntialias>().get(klass);
+PropertyValue<bool> FillLayer::getFillAntialias() const {
+ return impl().paint.template get<FillAntialias>().value;
}
-void FillLayer::setFillAntialias(PropertyValue<bool> value, const optional<std::string>& klass) {
- if (value == getFillAntialias(klass))
+void FillLayer::setFillAntialias(PropertyValue<bool> value) {
+ if (value == getFillAntialias())
return;
- impl->cascading.template get<FillAntialias>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillAntialias>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillLayer::setFillAntialiasTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillAntialias>().setTransition(value, klass);
+void FillLayer::setFillAntialiasTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillAntialias>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillLayer::getFillAntialiasTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillAntialias>().getTransition(klass);
+TransitionOptions FillLayer::getFillAntialiasTransition() const {
+ return impl().paint.template get<FillAntialias>().options;
}
DataDrivenPropertyValue<float> FillLayer::getDefaultFillOpacity() {
return { 1 };
}
-DataDrivenPropertyValue<float> FillLayer::getFillOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillOpacity>().get(klass);
+DataDrivenPropertyValue<float> FillLayer::getFillOpacity() const {
+ return impl().paint.template get<FillOpacity>().value;
}
-void FillLayer::setFillOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getFillOpacity(klass))
+void FillLayer::setFillOpacity(DataDrivenPropertyValue<float> value) {
+ if (value == getFillOpacity())
return;
- impl->cascading.template get<FillOpacity>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillLayer::setFillOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillOpacity>().setTransition(value, klass);
+void FillLayer::setFillOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillLayer::getFillOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillOpacity>().getTransition(klass);
+TransitionOptions FillLayer::getFillOpacityTransition() const {
+ return impl().paint.template get<FillOpacity>().options;
}
DataDrivenPropertyValue<Color> FillLayer::getDefaultFillColor() {
return { Color::black() };
}
-DataDrivenPropertyValue<Color> FillLayer::getFillColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillColor>().get(klass);
+DataDrivenPropertyValue<Color> FillLayer::getFillColor() const {
+ return impl().paint.template get<FillColor>().value;
}
-void FillLayer::setFillColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getFillColor(klass))
+void FillLayer::setFillColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getFillColor())
return;
- impl->cascading.template get<FillColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillLayer::setFillColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillColor>().setTransition(value, klass);
+void FillLayer::setFillColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillLayer::getFillColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillColor>().getTransition(klass);
+TransitionOptions FillLayer::getFillColorTransition() const {
+ return impl().paint.template get<FillColor>().options;
}
DataDrivenPropertyValue<Color> FillLayer::getDefaultFillOutlineColor() {
return { {} };
}
-DataDrivenPropertyValue<Color> FillLayer::getFillOutlineColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillOutlineColor>().get(klass);
+DataDrivenPropertyValue<Color> FillLayer::getFillOutlineColor() const {
+ return impl().paint.template get<FillOutlineColor>().value;
}
-void FillLayer::setFillOutlineColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getFillOutlineColor(klass))
+void FillLayer::setFillOutlineColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getFillOutlineColor())
return;
- impl->cascading.template get<FillOutlineColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillOutlineColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillLayer::setFillOutlineColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillOutlineColor>().setTransition(value, klass);
+void FillLayer::setFillOutlineColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillOutlineColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillLayer::getFillOutlineColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillOutlineColor>().getTransition(klass);
+TransitionOptions FillLayer::getFillOutlineColorTransition() const {
+ return impl().paint.template get<FillOutlineColor>().options;
}
PropertyValue<std::array<float, 2>> FillLayer::getDefaultFillTranslate() {
return { {{ 0, 0 }} };
}
-PropertyValue<std::array<float, 2>> FillLayer::getFillTranslate(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillTranslate>().get(klass);
+PropertyValue<std::array<float, 2>> FillLayer::getFillTranslate() const {
+ return impl().paint.template get<FillTranslate>().value;
}
-void FillLayer::setFillTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
- if (value == getFillTranslate(klass))
+void FillLayer::setFillTranslate(PropertyValue<std::array<float, 2>> value) {
+ if (value == getFillTranslate())
return;
- impl->cascading.template get<FillTranslate>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillTranslate>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillLayer::setFillTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillTranslate>().setTransition(value, klass);
+void FillLayer::setFillTranslateTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillTranslate>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillLayer::getFillTranslateTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillTranslate>().getTransition(klass);
+TransitionOptions FillLayer::getFillTranslateTransition() const {
+ return impl().paint.template get<FillTranslate>().options;
}
PropertyValue<TranslateAnchorType> FillLayer::getDefaultFillTranslateAnchor() {
return { TranslateAnchorType::Map };
}
-PropertyValue<TranslateAnchorType> FillLayer::getFillTranslateAnchor(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillTranslateAnchor>().get(klass);
+PropertyValue<TranslateAnchorType> FillLayer::getFillTranslateAnchor() const {
+ return impl().paint.template get<FillTranslateAnchor>().value;
}
-void FillLayer::setFillTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
- if (value == getFillTranslateAnchor(klass))
+void FillLayer::setFillTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
+ if (value == getFillTranslateAnchor())
return;
- impl->cascading.template get<FillTranslateAnchor>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillTranslateAnchor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillLayer::setFillTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillTranslateAnchor>().setTransition(value, klass);
+void FillLayer::setFillTranslateAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillTranslateAnchor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillLayer::getFillTranslateAnchorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillTranslateAnchor>().getTransition(klass);
+TransitionOptions FillLayer::getFillTranslateAnchorTransition() const {
+ return impl().paint.template get<FillTranslateAnchor>().options;
}
PropertyValue<std::string> FillLayer::getDefaultFillPattern() {
return { "" };
}
-PropertyValue<std::string> FillLayer::getFillPattern(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillPattern>().get(klass);
+PropertyValue<std::string> FillLayer::getFillPattern() const {
+ return impl().paint.template get<FillPattern>().value;
}
-void FillLayer::setFillPattern(PropertyValue<std::string> value, const optional<std::string>& klass) {
- if (value == getFillPattern(klass))
+void FillLayer::setFillPattern(PropertyValue<std::string> value) {
+ if (value == getFillPattern())
return;
- impl->cascading.template get<FillPattern>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillPattern>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void FillLayer::setFillPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<FillPattern>().setTransition(value, klass);
+void FillLayer::setFillPatternTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<FillPattern>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions FillLayer::getFillPatternTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<FillPattern>().getTransition(klass);
+TransitionOptions FillLayer::getFillPatternTransition() const {
+ return impl().paint.template get<FillPattern>().options;
}
} // namespace style
diff --git a/src/mbgl/style/layers/fill_layer_impl.cpp b/src/mbgl/style/layers/fill_layer_impl.cpp
index 6ec55a58e3..0dc7ed14d5 100644
--- a/src/mbgl/style/layers/fill_layer_impl.cpp
+++ b/src/mbgl/style/layers/fill_layer_impl.cpp
@@ -1,11 +1,14 @@
#include <mbgl/style/layers/fill_layer_impl.hpp>
-#include <mbgl/renderer/render_fill_layer.hpp>
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> FillLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderFillLayer>(*this);
+bool FillLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
+ assert(dynamic_cast<const FillLayer::Impl*>(&other));
+ const auto& impl = static_cast<const style::FillLayer::Impl&>(other);
+ return filter != impl.filter ||
+ visibility != impl.visibility ||
+ paint.hasDataDrivenPropertyDifference(impl.paint);
}
} // namespace style
diff --git a/src/mbgl/style/layers/fill_layer_impl.hpp b/src/mbgl/style/layers/fill_layer_impl.hpp
index 215558962e..2673cd7443 100644
--- a/src/mbgl/style/layers/fill_layer_impl.hpp
+++ b/src/mbgl/style/layers/fill_layer_impl.hpp
@@ -9,13 +9,12 @@ namespace style {
class FillLayer::Impl : public Layer::Impl {
public:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
- void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+ using Layer::Impl::Impl;
- std::unique_ptr<RenderLayer> createRenderLayer() const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- FillPaintProperties::Cascading cascading;
+ FillPaintProperties::Transitionable paint;
};
} // namespace style
diff --git a/src/mbgl/style/layers/fill_layer_properties.hpp b/src/mbgl/style/layers/fill_layer_properties.hpp
index ee1e5158ce..cb01194515 100644
--- a/src/mbgl/style/layers/fill_layer_properties.hpp
+++ b/src/mbgl/style/layers/fill_layer_properties.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
@@ -39,7 +40,7 @@ struct FillPattern : CrossFadedPaintProperty<std::string> {
static std::string defaultValue() { return ""; }
};
-class FillPaintProperties : public PaintProperties<
+class FillPaintProperties : public Properties<
FillAntialias,
FillOpacity,
FillColor,
diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs
index 2f690c3158..573aabda8b 100644
--- a/src/mbgl/style/layers/layer.cpp.ejs
+++ b/src/mbgl/style/layers/layer.cpp.ejs
@@ -7,47 +7,45 @@
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp>
#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
<% if (type === 'background') { -%>
<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID)
- : Layer(LayerType::<%- camelize(type) %>, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
+ : Layer(makeMutable<Impl>(LayerType::<%- camelize(type) %>, layerID, std::string())) {
}
<% } else { -%>
<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID, const std::string& sourceID)
- : Layer(LayerType::<%- camelize(type) %>, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
- impl->source = sourceID;
+ : Layer(makeMutable<Impl>(LayerType::<%- camelize(type) %>, layerID, sourceID)) {
}
<% } -%>
-<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const Impl& other)
- : Layer(LayerType::<%- camelize(type) %>, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
<%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default;
-std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::clone() const {
- return std::make_unique<<%- camelize(type) %>Layer>(*this);
+const <%- camelize(type) %>Layer::Impl& <%- camelize(type) %>Layer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<<%- camelize(type) %>Layer>(*this);
- result->impl->id = id_;
- result->impl->cascading = <%- camelize(type) %>PaintProperties::Cascading();
- return std::move(result);
+Mutable<<%- camelize(type) %>Layer::Impl> <%- camelize(type) %>Layer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> <%- camelize(type) %>Layer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = <%- camelize(type) %>PaintProperties::Transitionable();
+ return std::make_unique<<%- camelize(type) %>Layer>(std::move(impl_));
}
<% if (layoutProperties.length) { -%>
void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const {
- conversion::stringify(writer, layout);
+ layout.stringify(writer);
}
<% } else { -%>
void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
@@ -58,31 +56,60 @@ void <%- camelize(type) %>Layer::Impl::stringifyLayout(rapidjson::Writer<rapidjs
// Source
const std::string& <%- camelize(type) %>Layer::getSourceID() const {
- return impl->source;
+ return impl().source;
}
<% if (type !== 'raster') { -%>
void <%- camelize(type) %>Layer::setSourceLayer(const std::string& sourceLayer) {
- impl->sourceLayer = sourceLayer;
+ auto impl_ = mutableImpl();
+ impl_->sourceLayer = sourceLayer;
+ baseImpl = std::move(impl_);
}
const std::string& <%- camelize(type) %>Layer::getSourceLayer() const {
- return impl->sourceLayer;
+ return impl().sourceLayer;
}
// Filter
void <%- camelize(type) %>Layer::setFilter(const Filter& filter) {
- impl->filter = filter;
- impl->observer->onLayerFilterChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->filter = filter;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
const Filter& <%- camelize(type) %>Layer::getFilter() const {
- return impl->filter;
+ return impl().filter;
}
<% } -%>
<% } -%>
+// Visibility
+
+void <%- camelize(type) %>Layer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void <%- camelize(type) %>Layer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void <%- camelize(type) %>Layer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+}
+
// Layout properties
<% for (const property of layoutProperties) { -%>
@@ -91,14 +118,16 @@ const Filter& <%- camelize(type) %>Layer::getFilter() const {
}
<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {
- return impl->layout.unevaluated.get<<%- camelize(property.name) %>>();
+ return impl().layout.get<<%- camelize(property.name) %>>();
}
void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value) {
if (value == get<%- camelize(property.name) %>())
return;
- impl->layout.unevaluated.get<<%- camelize(property.name) %>>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "<%- property.name %>");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<<%- camelize(property.name) %>>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
<% } -%>
@@ -108,31 +137,27 @@ void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyV
return { <%- defaultValue(property) %> };
}
-<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>(const optional<std::string>& klass) const {
- return impl->cascading.template get<<%- camelize(property.name) %>>().get(klass);
+<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {
+ return impl().paint.template get<<%- camelize(property.name) %>>().value;
}
-void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value, const optional<std::string>& klass) {
- if (value == get<%- camelize(property.name) %>(klass))
+void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value) {
+ if (value == get<%- camelize(property.name) %>())
return;
- impl->cascading.template get<<%- camelize(property.name) %>>().set(value, klass);
-<% if (isDataDriven(property)) { -%>
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
-<% } else { -%>
- impl->observer->onLayerPaintPropertyChanged(*this);
-<% } -%>
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<<%- camelize(property.name) %>>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>Transition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<<%- camelize(property.name) %>>().setTransition(value, klass);
+void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>Transition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<<%- camelize(property.name) %>>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions <%- camelize(type) %>Layer::get<%- camelize(property.name) %>Transition(const optional<std::string>& klass) const {
- return impl->cascading.template get<<%- camelize(property.name) %>>().getTransition(klass);
+TransitionOptions <%- camelize(type) %>Layer::get<%- camelize(property.name) %>Transition() const {
+ return impl().paint.template get<<%- camelize(property.name) %>>().options;
}
<% } -%>
diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs
index 2a736ca388..cde1b80b7b 100644
--- a/src/mbgl/style/layers/layer_properties.hpp.ejs
+++ b/src/mbgl/style/layers/layer_properties.hpp.ejs
@@ -10,7 +10,9 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
namespace mbgl {
namespace style {
@@ -29,7 +31,7 @@ struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %>
<% } -%>
<% if (layoutProperties.length) { -%>
-class <%- camelize(type) %>LayoutProperties : public LayoutProperties<
+class <%- camelize(type) %>LayoutProperties : public Properties<
<% for (const property of layoutProperties.slice(0, -1)) { -%>
<%- camelize(property.name) %>,
<% } -%>
@@ -37,7 +39,7 @@ class <%- camelize(type) %>LayoutProperties : public LayoutProperties<
> {};
<% } -%>
-class <%- camelize(type) %>PaintProperties : public PaintProperties<
+class <%- camelize(type) %>PaintProperties : public Properties<
<% for (const property of paintProperties.slice(0, -1)) { -%>
<%- camelize(property.name) %>,
<% } -%>
diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp
index 7f1575aad5..1c7f0d28ee 100644
--- a/src/mbgl/style/layers/line_layer.cpp
+++ b/src/mbgl/style/layers/line_layer.cpp
@@ -2,63 +2,92 @@
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
LineLayer::LineLayer(const std::string& layerID, const std::string& sourceID)
- : Layer(LayerType::Line, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
- impl->source = sourceID;
+ : Layer(makeMutable<Impl>(LayerType::Line, layerID, sourceID)) {
}
-LineLayer::LineLayer(const Impl& other)
- : Layer(LayerType::Line, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+LineLayer::LineLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
LineLayer::~LineLayer() = default;
-std::unique_ptr<Layer> LineLayer::Impl::clone() const {
- return std::make_unique<LineLayer>(*this);
+const LineLayer::Impl& LineLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> LineLayer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<LineLayer>(*this);
- result->impl->id = id_;
- result->impl->cascading = LinePaintProperties::Cascading();
- return std::move(result);
+Mutable<LineLayer::Impl> LineLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> LineLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = LinePaintProperties::Transitionable();
+ return std::make_unique<LineLayer>(std::move(impl_));
}
void LineLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const {
- conversion::stringify(writer, layout);
+ layout.stringify(writer);
}
// Source
const std::string& LineLayer::getSourceID() const {
- return impl->source;
+ return impl().source;
}
void LineLayer::setSourceLayer(const std::string& sourceLayer) {
- impl->sourceLayer = sourceLayer;
+ auto impl_ = mutableImpl();
+ impl_->sourceLayer = sourceLayer;
+ baseImpl = std::move(impl_);
}
const std::string& LineLayer::getSourceLayer() const {
- return impl->sourceLayer;
+ return impl().sourceLayer;
}
// Filter
void LineLayer::setFilter(const Filter& filter) {
- impl->filter = filter;
- impl->observer->onLayerFilterChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->filter = filter;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
const Filter& LineLayer::getFilter() const {
- return impl->filter;
+ return impl().filter;
+}
+
+// Visibility
+
+void LineLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void LineLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void LineLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
}
// Layout properties
@@ -68,56 +97,64 @@ PropertyValue<LineCapType> LineLayer::getDefaultLineCap() {
}
PropertyValue<LineCapType> LineLayer::getLineCap() const {
- return impl->layout.unevaluated.get<LineCap>();
+ return impl().layout.get<LineCap>();
}
void LineLayer::setLineCap(PropertyValue<LineCapType> value) {
if (value == getLineCap())
return;
- impl->layout.unevaluated.get<LineCap>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "line-cap");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<LineCap>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-PropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() {
+DataDrivenPropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() {
return LineJoin::defaultValue();
}
-PropertyValue<LineJoinType> LineLayer::getLineJoin() const {
- return impl->layout.unevaluated.get<LineJoin>();
+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;
- impl->layout.unevaluated.get<LineJoin>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "line-join");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<LineJoin>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<float> LineLayer::getDefaultLineMiterLimit() {
return LineMiterLimit::defaultValue();
}
PropertyValue<float> LineLayer::getLineMiterLimit() const {
- return impl->layout.unevaluated.get<LineMiterLimit>();
+ return impl().layout.get<LineMiterLimit>();
}
void LineLayer::setLineMiterLimit(PropertyValue<float> value) {
if (value == getLineMiterLimit())
return;
- impl->layout.unevaluated.get<LineMiterLimit>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "line-miter-limit");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<LineMiterLimit>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<float> LineLayer::getDefaultLineRoundLimit() {
return LineRoundLimit::defaultValue();
}
PropertyValue<float> LineLayer::getLineRoundLimit() const {
- return impl->layout.unevaluated.get<LineRoundLimit>();
+ return impl().layout.get<LineRoundLimit>();
}
void LineLayer::setLineRoundLimit(PropertyValue<float> value) {
if (value == getLineRoundLimit())
return;
- impl->layout.unevaluated.get<LineRoundLimit>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "line-round-limit");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<LineRoundLimit>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Paint properties
@@ -126,250 +163,270 @@ DataDrivenPropertyValue<float> LineLayer::getDefaultLineOpacity() {
return { 1 };
}
-DataDrivenPropertyValue<float> LineLayer::getLineOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineOpacity>().get(klass);
+DataDrivenPropertyValue<float> LineLayer::getLineOpacity() const {
+ return impl().paint.template get<LineOpacity>().value;
}
-void LineLayer::setLineOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getLineOpacity(klass))
+void LineLayer::setLineOpacity(DataDrivenPropertyValue<float> value) {
+ if (value == getLineOpacity())
return;
- impl->cascading.template get<LineOpacity>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineOpacity>().setTransition(value, klass);
+void LineLayer::setLineOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineOpacity>().getTransition(klass);
+TransitionOptions LineLayer::getLineOpacityTransition() const {
+ return impl().paint.template get<LineOpacity>().options;
}
DataDrivenPropertyValue<Color> LineLayer::getDefaultLineColor() {
return { Color::black() };
}
-DataDrivenPropertyValue<Color> LineLayer::getLineColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineColor>().get(klass);
+DataDrivenPropertyValue<Color> LineLayer::getLineColor() const {
+ return impl().paint.template get<LineColor>().value;
}
-void LineLayer::setLineColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getLineColor(klass))
+void LineLayer::setLineColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getLineColor())
return;
- impl->cascading.template get<LineColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineColor>().setTransition(value, klass);
+void LineLayer::setLineColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineColor>().getTransition(klass);
+TransitionOptions LineLayer::getLineColorTransition() const {
+ return impl().paint.template get<LineColor>().options;
}
PropertyValue<std::array<float, 2>> LineLayer::getDefaultLineTranslate() {
return { {{ 0, 0 }} };
}
-PropertyValue<std::array<float, 2>> LineLayer::getLineTranslate(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineTranslate>().get(klass);
+PropertyValue<std::array<float, 2>> LineLayer::getLineTranslate() const {
+ return impl().paint.template get<LineTranslate>().value;
}
-void LineLayer::setLineTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
- if (value == getLineTranslate(klass))
+void LineLayer::setLineTranslate(PropertyValue<std::array<float, 2>> value) {
+ if (value == getLineTranslate())
return;
- impl->cascading.template get<LineTranslate>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineTranslate>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineTranslate>().setTransition(value, klass);
+void LineLayer::setLineTranslateTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineTranslate>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineTranslateTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineTranslate>().getTransition(klass);
+TransitionOptions LineLayer::getLineTranslateTransition() const {
+ return impl().paint.template get<LineTranslate>().options;
}
PropertyValue<TranslateAnchorType> LineLayer::getDefaultLineTranslateAnchor() {
return { TranslateAnchorType::Map };
}
-PropertyValue<TranslateAnchorType> LineLayer::getLineTranslateAnchor(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineTranslateAnchor>().get(klass);
+PropertyValue<TranslateAnchorType> LineLayer::getLineTranslateAnchor() const {
+ return impl().paint.template get<LineTranslateAnchor>().value;
}
-void LineLayer::setLineTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
- if (value == getLineTranslateAnchor(klass))
+void LineLayer::setLineTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
+ if (value == getLineTranslateAnchor())
return;
- impl->cascading.template get<LineTranslateAnchor>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineTranslateAnchor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineTranslateAnchor>().setTransition(value, klass);
+void LineLayer::setLineTranslateAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineTranslateAnchor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineTranslateAnchorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineTranslateAnchor>().getTransition(klass);
+TransitionOptions LineLayer::getLineTranslateAnchorTransition() const {
+ return impl().paint.template get<LineTranslateAnchor>().options;
}
-PropertyValue<float> LineLayer::getDefaultLineWidth() {
+DataDrivenPropertyValue<float> LineLayer::getDefaultLineWidth() {
return { 1 };
}
-PropertyValue<float> LineLayer::getLineWidth(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineWidth>().get(klass);
+DataDrivenPropertyValue<float> LineLayer::getLineWidth() const {
+ return impl().paint.template get<LineWidth>().value;
}
-void LineLayer::setLineWidth(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getLineWidth(klass))
+void LineLayer::setLineWidth(DataDrivenPropertyValue<float> value) {
+ if (value == getLineWidth())
return;
- impl->cascading.template get<LineWidth>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineWidth>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineWidth>().setTransition(value, klass);
+void LineLayer::setLineWidthTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineWidth>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineWidthTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineWidth>().getTransition(klass);
+TransitionOptions LineLayer::getLineWidthTransition() const {
+ return impl().paint.template get<LineWidth>().options;
}
DataDrivenPropertyValue<float> LineLayer::getDefaultLineGapWidth() {
return { 0 };
}
-DataDrivenPropertyValue<float> LineLayer::getLineGapWidth(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineGapWidth>().get(klass);
+DataDrivenPropertyValue<float> LineLayer::getLineGapWidth() const {
+ return impl().paint.template get<LineGapWidth>().value;
}
-void LineLayer::setLineGapWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getLineGapWidth(klass))
+void LineLayer::setLineGapWidth(DataDrivenPropertyValue<float> value) {
+ if (value == getLineGapWidth())
return;
- impl->cascading.template get<LineGapWidth>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineGapWidth>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineGapWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineGapWidth>().setTransition(value, klass);
+void LineLayer::setLineGapWidthTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineGapWidth>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineGapWidthTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineGapWidth>().getTransition(klass);
+TransitionOptions LineLayer::getLineGapWidthTransition() const {
+ return impl().paint.template get<LineGapWidth>().options;
}
DataDrivenPropertyValue<float> LineLayer::getDefaultLineOffset() {
return { 0 };
}
-DataDrivenPropertyValue<float> LineLayer::getLineOffset(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineOffset>().get(klass);
+DataDrivenPropertyValue<float> LineLayer::getLineOffset() const {
+ return impl().paint.template get<LineOffset>().value;
}
-void LineLayer::setLineOffset(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getLineOffset(klass))
+void LineLayer::setLineOffset(DataDrivenPropertyValue<float> value) {
+ if (value == getLineOffset())
return;
- impl->cascading.template get<LineOffset>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineOffset>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineOffsetTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineOffset>().setTransition(value, klass);
+void LineLayer::setLineOffsetTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineOffset>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineOffsetTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineOffset>().getTransition(klass);
+TransitionOptions LineLayer::getLineOffsetTransition() const {
+ return impl().paint.template get<LineOffset>().options;
}
DataDrivenPropertyValue<float> LineLayer::getDefaultLineBlur() {
return { 0 };
}
-DataDrivenPropertyValue<float> LineLayer::getLineBlur(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineBlur>().get(klass);
+DataDrivenPropertyValue<float> LineLayer::getLineBlur() const {
+ return impl().paint.template get<LineBlur>().value;
}
-void LineLayer::setLineBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getLineBlur(klass))
+void LineLayer::setLineBlur(DataDrivenPropertyValue<float> value) {
+ if (value == getLineBlur())
return;
- impl->cascading.template get<LineBlur>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineBlur>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineBlur>().setTransition(value, klass);
+void LineLayer::setLineBlurTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineBlur>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineBlurTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineBlur>().getTransition(klass);
+TransitionOptions LineLayer::getLineBlurTransition() const {
+ return impl().paint.template get<LineBlur>().options;
}
PropertyValue<std::vector<float>> LineLayer::getDefaultLineDasharray() {
return { { } };
}
-PropertyValue<std::vector<float>> LineLayer::getLineDasharray(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineDasharray>().get(klass);
+PropertyValue<std::vector<float>> LineLayer::getLineDasharray() const {
+ return impl().paint.template get<LineDasharray>().value;
}
-void LineLayer::setLineDasharray(PropertyValue<std::vector<float>> value, const optional<std::string>& klass) {
- if (value == getLineDasharray(klass))
+void LineLayer::setLineDasharray(PropertyValue<std::vector<float>> value) {
+ if (value == getLineDasharray())
return;
- impl->cascading.template get<LineDasharray>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineDasharray>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLineDasharrayTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LineDasharray>().setTransition(value, klass);
+void LineLayer::setLineDasharrayTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LineDasharray>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLineDasharrayTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LineDasharray>().getTransition(klass);
+TransitionOptions LineLayer::getLineDasharrayTransition() const {
+ return impl().paint.template get<LineDasharray>().options;
}
PropertyValue<std::string> LineLayer::getDefaultLinePattern() {
return { "" };
}
-PropertyValue<std::string> LineLayer::getLinePattern(const optional<std::string>& klass) const {
- return impl->cascading.template get<LinePattern>().get(klass);
+PropertyValue<std::string> LineLayer::getLinePattern() const {
+ return impl().paint.template get<LinePattern>().value;
}
-void LineLayer::setLinePattern(PropertyValue<std::string> value, const optional<std::string>& klass) {
- if (value == getLinePattern(klass))
+void LineLayer::setLinePattern(PropertyValue<std::string> value) {
+ if (value == getLinePattern())
return;
- impl->cascading.template get<LinePattern>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LinePattern>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void LineLayer::setLinePatternTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<LinePattern>().setTransition(value, klass);
+void LineLayer::setLinePatternTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<LinePattern>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions LineLayer::getLinePatternTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<LinePattern>().getTransition(klass);
+TransitionOptions LineLayer::getLinePatternTransition() const {
+ return impl().paint.template get<LinePattern>().options;
}
} // namespace style
diff --git a/src/mbgl/style/layers/line_layer_impl.cpp b/src/mbgl/style/layers/line_layer_impl.cpp
index 973a77abf4..bee88d6a47 100644
--- a/src/mbgl/style/layers/line_layer_impl.cpp
+++ b/src/mbgl/style/layers/line_layer_impl.cpp
@@ -1,11 +1,15 @@
#include <mbgl/style/layers/line_layer_impl.hpp>
-#include <mbgl/renderer/render_line_layer.hpp>
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> LineLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderLineLayer>(*this);
+bool LineLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
+ assert(dynamic_cast<const LineLayer::Impl*>(&other));
+ const auto& impl = static_cast<const style::LineLayer::Impl&>(other);
+ return filter != impl.filter ||
+ visibility != impl.visibility ||
+ layout != impl.layout ||
+ paint.hasDataDrivenPropertyDifference(impl.paint);
}
} // namespace style
diff --git a/src/mbgl/style/layers/line_layer_impl.hpp b/src/mbgl/style/layers/line_layer_impl.hpp
index 02c9c85f00..04adc0e85c 100644
--- a/src/mbgl/style/layers/line_layer_impl.hpp
+++ b/src/mbgl/style/layers/line_layer_impl.hpp
@@ -9,14 +9,13 @@ namespace style {
class LineLayer::Impl : public Layer::Impl {
public:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
- void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+ using Layer::Impl::Impl;
- std::unique_ptr<RenderLayer> createRenderLayer() const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- LineLayoutProperties layout;
- LinePaintProperties::Cascading cascading;
+ LineLayoutProperties::Unevaluated layout;
+ LinePaintProperties::Transitionable paint;
};
} // namespace style
diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp
index 6c301e6a0e..aeaf51698a 100644
--- a/src/mbgl/style/layers/line_layer_properties.hpp
+++ b/src/mbgl/style/layers/line_layer_properties.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
@@ -16,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; }
};
@@ -47,7 +48,7 @@ struct LineTranslateAnchor : PaintProperty<TranslateAnchorType> {
static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; }
};
-struct LineWidth : PaintProperty<float> {
+struct LineWidth : DataDrivenPaintProperty<float, attributes::a_width, uniforms::u_width> {
static float defaultValue() { return 1; }
};
@@ -71,14 +72,14 @@ struct LinePattern : CrossFadedPaintProperty<std::string> {
static std::string defaultValue() { return ""; }
};
-class LineLayoutProperties : public LayoutProperties<
+class LineLayoutProperties : public Properties<
LineCap,
LineJoin,
LineMiterLimit,
LineRoundLimit
> {};
-class LinePaintProperties : public PaintProperties<
+class LinePaintProperties : public Properties<
LineOpacity,
LineColor,
LineTranslate,
diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp
index b525f9eaa4..a9a8d273fa 100644
--- a/src/mbgl/style/layers/raster_layer.cpp
+++ b/src/mbgl/style/layers/raster_layer.cpp
@@ -2,34 +2,34 @@
#include <mbgl/style/layers/raster_layer.hpp>
#include <mbgl/style/layers/raster_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
RasterLayer::RasterLayer(const std::string& layerID, const std::string& sourceID)
- : Layer(LayerType::Raster, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
- impl->source = sourceID;
+ : Layer(makeMutable<Impl>(LayerType::Raster, layerID, sourceID)) {
}
-RasterLayer::RasterLayer(const Impl& other)
- : Layer(LayerType::Raster, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+RasterLayer::RasterLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
RasterLayer::~RasterLayer() = default;
-std::unique_ptr<Layer> RasterLayer::Impl::clone() const {
- return std::make_unique<RasterLayer>(*this);
+const RasterLayer::Impl& RasterLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> RasterLayer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<RasterLayer>(*this);
- result->impl->id = id_;
- result->impl->cascading = RasterPaintProperties::Cascading();
- return std::move(result);
+Mutable<RasterLayer::Impl> RasterLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> RasterLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = RasterPaintProperties::Transitionable();
+ return std::make_unique<RasterLayer>(std::move(impl_));
}
void RasterLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
@@ -38,10 +38,35 @@ void RasterLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffe
// Source
const std::string& RasterLayer::getSourceID() const {
- return impl->source;
+ return impl().source;
}
+// Visibility
+
+void RasterLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void RasterLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void RasterLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+}
+
// Layout properties
@@ -51,161 +76,189 @@ PropertyValue<float> RasterLayer::getDefaultRasterOpacity() {
return { 1 };
}
-PropertyValue<float> RasterLayer::getRasterOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterOpacity>().get(klass);
+PropertyValue<float> RasterLayer::getRasterOpacity() const {
+ return impl().paint.template get<RasterOpacity>().value;
}
-void RasterLayer::setRasterOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getRasterOpacity(klass))
+void RasterLayer::setRasterOpacity(PropertyValue<float> value) {
+ if (value == getRasterOpacity())
return;
- impl->cascading.template get<RasterOpacity>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void RasterLayer::setRasterOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<RasterOpacity>().setTransition(value, klass);
+void RasterLayer::setRasterOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions RasterLayer::getRasterOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterOpacity>().getTransition(klass);
+TransitionOptions RasterLayer::getRasterOpacityTransition() const {
+ return impl().paint.template get<RasterOpacity>().options;
}
PropertyValue<float> RasterLayer::getDefaultRasterHueRotate() {
return { 0 };
}
-PropertyValue<float> RasterLayer::getRasterHueRotate(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterHueRotate>().get(klass);
+PropertyValue<float> RasterLayer::getRasterHueRotate() const {
+ return impl().paint.template get<RasterHueRotate>().value;
}
-void RasterLayer::setRasterHueRotate(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getRasterHueRotate(klass))
+void RasterLayer::setRasterHueRotate(PropertyValue<float> value) {
+ if (value == getRasterHueRotate())
return;
- impl->cascading.template get<RasterHueRotate>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterHueRotate>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void RasterLayer::setRasterHueRotateTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<RasterHueRotate>().setTransition(value, klass);
+void RasterLayer::setRasterHueRotateTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterHueRotate>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions RasterLayer::getRasterHueRotateTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterHueRotate>().getTransition(klass);
+TransitionOptions RasterLayer::getRasterHueRotateTransition() const {
+ return impl().paint.template get<RasterHueRotate>().options;
}
PropertyValue<float> RasterLayer::getDefaultRasterBrightnessMin() {
return { 0 };
}
-PropertyValue<float> RasterLayer::getRasterBrightnessMin(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterBrightnessMin>().get(klass);
+PropertyValue<float> RasterLayer::getRasterBrightnessMin() const {
+ return impl().paint.template get<RasterBrightnessMin>().value;
}
-void RasterLayer::setRasterBrightnessMin(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getRasterBrightnessMin(klass))
+void RasterLayer::setRasterBrightnessMin(PropertyValue<float> value) {
+ if (value == getRasterBrightnessMin())
return;
- impl->cascading.template get<RasterBrightnessMin>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterBrightnessMin>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void RasterLayer::setRasterBrightnessMinTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<RasterBrightnessMin>().setTransition(value, klass);
+void RasterLayer::setRasterBrightnessMinTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterBrightnessMin>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions RasterLayer::getRasterBrightnessMinTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterBrightnessMin>().getTransition(klass);
+TransitionOptions RasterLayer::getRasterBrightnessMinTransition() const {
+ return impl().paint.template get<RasterBrightnessMin>().options;
}
PropertyValue<float> RasterLayer::getDefaultRasterBrightnessMax() {
return { 1 };
}
-PropertyValue<float> RasterLayer::getRasterBrightnessMax(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterBrightnessMax>().get(klass);
+PropertyValue<float> RasterLayer::getRasterBrightnessMax() const {
+ return impl().paint.template get<RasterBrightnessMax>().value;
}
-void RasterLayer::setRasterBrightnessMax(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getRasterBrightnessMax(klass))
+void RasterLayer::setRasterBrightnessMax(PropertyValue<float> value) {
+ if (value == getRasterBrightnessMax())
return;
- impl->cascading.template get<RasterBrightnessMax>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterBrightnessMax>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void RasterLayer::setRasterBrightnessMaxTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<RasterBrightnessMax>().setTransition(value, klass);
+void RasterLayer::setRasterBrightnessMaxTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterBrightnessMax>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions RasterLayer::getRasterBrightnessMaxTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterBrightnessMax>().getTransition(klass);
+TransitionOptions RasterLayer::getRasterBrightnessMaxTransition() const {
+ return impl().paint.template get<RasterBrightnessMax>().options;
}
PropertyValue<float> RasterLayer::getDefaultRasterSaturation() {
return { 0 };
}
-PropertyValue<float> RasterLayer::getRasterSaturation(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterSaturation>().get(klass);
+PropertyValue<float> RasterLayer::getRasterSaturation() const {
+ return impl().paint.template get<RasterSaturation>().value;
}
-void RasterLayer::setRasterSaturation(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getRasterSaturation(klass))
+void RasterLayer::setRasterSaturation(PropertyValue<float> value) {
+ if (value == getRasterSaturation())
return;
- impl->cascading.template get<RasterSaturation>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterSaturation>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void RasterLayer::setRasterSaturationTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<RasterSaturation>().setTransition(value, klass);
+void RasterLayer::setRasterSaturationTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterSaturation>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions RasterLayer::getRasterSaturationTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterSaturation>().getTransition(klass);
+TransitionOptions RasterLayer::getRasterSaturationTransition() const {
+ return impl().paint.template get<RasterSaturation>().options;
}
PropertyValue<float> RasterLayer::getDefaultRasterContrast() {
return { 0 };
}
-PropertyValue<float> RasterLayer::getRasterContrast(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterContrast>().get(klass);
+PropertyValue<float> RasterLayer::getRasterContrast() const {
+ return impl().paint.template get<RasterContrast>().value;
}
-void RasterLayer::setRasterContrast(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getRasterContrast(klass))
+void RasterLayer::setRasterContrast(PropertyValue<float> value) {
+ if (value == getRasterContrast())
return;
- impl->cascading.template get<RasterContrast>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterContrast>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void RasterLayer::setRasterContrastTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<RasterContrast>().setTransition(value, klass);
+void RasterLayer::setRasterContrastTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterContrast>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions RasterLayer::getRasterContrastTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterContrast>().getTransition(klass);
+TransitionOptions RasterLayer::getRasterContrastTransition() const {
+ return impl().paint.template get<RasterContrast>().options;
}
PropertyValue<float> RasterLayer::getDefaultRasterFadeDuration() {
return { 300 };
}
-PropertyValue<float> RasterLayer::getRasterFadeDuration(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterFadeDuration>().get(klass);
+PropertyValue<float> RasterLayer::getRasterFadeDuration() const {
+ return impl().paint.template get<RasterFadeDuration>().value;
}
-void RasterLayer::setRasterFadeDuration(PropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getRasterFadeDuration(klass))
+void RasterLayer::setRasterFadeDuration(PropertyValue<float> value) {
+ if (value == getRasterFadeDuration())
return;
- impl->cascading.template get<RasterFadeDuration>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterFadeDuration>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void RasterLayer::setRasterFadeDurationTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<RasterFadeDuration>().setTransition(value, klass);
+void RasterLayer::setRasterFadeDurationTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<RasterFadeDuration>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions RasterLayer::getRasterFadeDurationTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<RasterFadeDuration>().getTransition(klass);
+TransitionOptions RasterLayer::getRasterFadeDurationTransition() const {
+ return impl().paint.template get<RasterFadeDuration>().options;
}
} // namespace style
diff --git a/src/mbgl/style/layers/raster_layer_impl.cpp b/src/mbgl/style/layers/raster_layer_impl.cpp
index fa9f80dac6..a995f50dd3 100644
--- a/src/mbgl/style/layers/raster_layer_impl.cpp
+++ b/src/mbgl/style/layers/raster_layer_impl.cpp
@@ -1,11 +1,10 @@
#include <mbgl/style/layers/raster_layer_impl.hpp>
-#include <mbgl/renderer/render_raster_layer.hpp>
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> RasterLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderRasterLayer>(*this);
+bool RasterLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
+ return false;
}
} // namespace style
diff --git a/src/mbgl/style/layers/raster_layer_impl.hpp b/src/mbgl/style/layers/raster_layer_impl.hpp
index edf5f9111b..adbe703e92 100644
--- a/src/mbgl/style/layers/raster_layer_impl.hpp
+++ b/src/mbgl/style/layers/raster_layer_impl.hpp
@@ -9,13 +9,12 @@ namespace style {
class RasterLayer::Impl : public Layer::Impl {
public:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
- void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+ using Layer::Impl::Impl;
- std::unique_ptr<RenderLayer> createRenderLayer() const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- RasterPaintProperties::Cascading cascading;
+ RasterPaintProperties::Transitionable paint;
};
} // namespace style
diff --git a/src/mbgl/style/layers/raster_layer_properties.hpp b/src/mbgl/style/layers/raster_layer_properties.hpp
index 219fe34d8c..12df09f32c 100644
--- a/src/mbgl/style/layers/raster_layer_properties.hpp
+++ b/src/mbgl/style/layers/raster_layer_properties.hpp
@@ -5,7 +5,9 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
namespace mbgl {
namespace style {
@@ -38,7 +40,7 @@ struct RasterFadeDuration : PaintProperty<float> {
static float defaultValue() { return 300; }
};
-class RasterPaintProperties : public PaintProperties<
+class RasterPaintProperties : public Properties<
RasterOpacity,
RasterHueRotate,
RasterBrightnessMin,
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 273a9fd24e..9a944657ca 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -2,63 +2,92 @@
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/style/layer_observer.hpp>
namespace mbgl {
namespace style {
SymbolLayer::SymbolLayer(const std::string& layerID, const std::string& sourceID)
- : Layer(LayerType::Symbol, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
- impl->source = sourceID;
+ : Layer(makeMutable<Impl>(LayerType::Symbol, layerID, sourceID)) {
}
-SymbolLayer::SymbolLayer(const Impl& other)
- : Layer(LayerType::Symbol, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+SymbolLayer::SymbolLayer(Immutable<Impl> impl_)
+ : Layer(std::move(impl_)) {
}
SymbolLayer::~SymbolLayer() = default;
-std::unique_ptr<Layer> SymbolLayer::Impl::clone() const {
- return std::make_unique<SymbolLayer>(*this);
+const SymbolLayer::Impl& SymbolLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-std::unique_ptr<Layer> SymbolLayer::Impl::cloneRef(const std::string& id_) const {
- auto result = std::make_unique<SymbolLayer>(*this);
- result->impl->id = id_;
- result->impl->cascading = SymbolPaintProperties::Cascading();
- return std::move(result);
+Mutable<SymbolLayer::Impl> SymbolLayer::mutableImpl() const {
+ return makeMutable<Impl>(impl());
+}
+
+std::unique_ptr<Layer> SymbolLayer::cloneRef(const std::string& id_) const {
+ auto impl_ = mutableImpl();
+ impl_->id = id_;
+ impl_->paint = SymbolPaintProperties::Transitionable();
+ return std::make_unique<SymbolLayer>(std::move(impl_));
}
void SymbolLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const {
- conversion::stringify(writer, layout);
+ layout.stringify(writer);
}
// Source
const std::string& SymbolLayer::getSourceID() const {
- return impl->source;
+ return impl().source;
}
void SymbolLayer::setSourceLayer(const std::string& sourceLayer) {
- impl->sourceLayer = sourceLayer;
+ auto impl_ = mutableImpl();
+ impl_->sourceLayer = sourceLayer;
+ baseImpl = std::move(impl_);
}
const std::string& SymbolLayer::getSourceLayer() const {
- return impl->sourceLayer;
+ return impl().sourceLayer;
}
// Filter
void SymbolLayer::setFilter(const Filter& filter) {
- impl->filter = filter;
- impl->observer->onLayerFilterChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->filter = filter;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
const Filter& SymbolLayer::getFilter() const {
- return impl->filter;
+ return impl().filter;
+}
+
+// Visibility
+
+void SymbolLayer::setVisibility(VisibilityType value) {
+ if (value == getVisibility())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->visibility = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// Zoom range
+
+void SymbolLayer::setMinZoom(float minZoom) {
+ auto impl_ = mutableImpl();
+ impl_->minZoom = minZoom;
+ baseImpl = std::move(impl_);
+}
+
+void SymbolLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
}
// Layout properties
@@ -68,476 +97,576 @@ PropertyValue<SymbolPlacementType> SymbolLayer::getDefaultSymbolPlacement() {
}
PropertyValue<SymbolPlacementType> SymbolLayer::getSymbolPlacement() const {
- return impl->layout.unevaluated.get<SymbolPlacement>();
+ return impl().layout.get<SymbolPlacement>();
}
void SymbolLayer::setSymbolPlacement(PropertyValue<SymbolPlacementType> value) {
if (value == getSymbolPlacement())
return;
- impl->layout.unevaluated.get<SymbolPlacement>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "symbol-placement");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<SymbolPlacement>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<float> SymbolLayer::getDefaultSymbolSpacing() {
return SymbolSpacing::defaultValue();
}
PropertyValue<float> SymbolLayer::getSymbolSpacing() const {
- return impl->layout.unevaluated.get<SymbolSpacing>();
+ return impl().layout.get<SymbolSpacing>();
}
void SymbolLayer::setSymbolSpacing(PropertyValue<float> value) {
if (value == getSymbolSpacing())
return;
- impl->layout.unevaluated.get<SymbolSpacing>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "symbol-spacing");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<SymbolSpacing>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultSymbolAvoidEdges() {
return SymbolAvoidEdges::defaultValue();
}
PropertyValue<bool> SymbolLayer::getSymbolAvoidEdges() const {
- return impl->layout.unevaluated.get<SymbolAvoidEdges>();
+ return impl().layout.get<SymbolAvoidEdges>();
}
void SymbolLayer::setSymbolAvoidEdges(PropertyValue<bool> value) {
if (value == getSymbolAvoidEdges())
return;
- impl->layout.unevaluated.get<SymbolAvoidEdges>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "symbol-avoid-edges");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<SymbolAvoidEdges>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultIconAllowOverlap() {
return IconAllowOverlap::defaultValue();
}
PropertyValue<bool> SymbolLayer::getIconAllowOverlap() const {
- return impl->layout.unevaluated.get<IconAllowOverlap>();
+ return impl().layout.get<IconAllowOverlap>();
}
void SymbolLayer::setIconAllowOverlap(PropertyValue<bool> value) {
if (value == getIconAllowOverlap())
return;
- impl->layout.unevaluated.get<IconAllowOverlap>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-allow-overlap");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconAllowOverlap>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultIconIgnorePlacement() {
return IconIgnorePlacement::defaultValue();
}
PropertyValue<bool> SymbolLayer::getIconIgnorePlacement() const {
- return impl->layout.unevaluated.get<IconIgnorePlacement>();
+ return impl().layout.get<IconIgnorePlacement>();
}
void SymbolLayer::setIconIgnorePlacement(PropertyValue<bool> value) {
if (value == getIconIgnorePlacement())
return;
- impl->layout.unevaluated.get<IconIgnorePlacement>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-ignore-placement");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconIgnorePlacement>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultIconOptional() {
return IconOptional::defaultValue();
}
PropertyValue<bool> SymbolLayer::getIconOptional() const {
- return impl->layout.unevaluated.get<IconOptional>();
+ return impl().layout.get<IconOptional>();
}
void SymbolLayer::setIconOptional(PropertyValue<bool> value) {
if (value == getIconOptional())
return;
- impl->layout.unevaluated.get<IconOptional>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-optional");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconOptional>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<AlignmentType> SymbolLayer::getDefaultIconRotationAlignment() {
return IconRotationAlignment::defaultValue();
}
PropertyValue<AlignmentType> SymbolLayer::getIconRotationAlignment() const {
- return impl->layout.unevaluated.get<IconRotationAlignment>();
+ return impl().layout.get<IconRotationAlignment>();
}
void SymbolLayer::setIconRotationAlignment(PropertyValue<AlignmentType> value) {
if (value == getIconRotationAlignment())
return;
- impl->layout.unevaluated.get<IconRotationAlignment>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-rotation-alignment");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconRotationAlignment>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconSize() {
return IconSize::defaultValue();
}
DataDrivenPropertyValue<float> SymbolLayer::getIconSize() const {
- return impl->layout.unevaluated.get<IconSize>();
+ return impl().layout.get<IconSize>();
}
void SymbolLayer::setIconSize(DataDrivenPropertyValue<float> value) {
if (value == getIconSize())
return;
- impl->layout.unevaluated.get<IconSize>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-size");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconSize>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<IconTextFitType> SymbolLayer::getDefaultIconTextFit() {
return IconTextFit::defaultValue();
}
PropertyValue<IconTextFitType> SymbolLayer::getIconTextFit() const {
- return impl->layout.unevaluated.get<IconTextFit>();
+ return impl().layout.get<IconTextFit>();
}
void SymbolLayer::setIconTextFit(PropertyValue<IconTextFitType> value) {
if (value == getIconTextFit())
return;
- impl->layout.unevaluated.get<IconTextFit>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-text-fit");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconTextFit>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<std::array<float, 4>> SymbolLayer::getDefaultIconTextFitPadding() {
return IconTextFitPadding::defaultValue();
}
PropertyValue<std::array<float, 4>> SymbolLayer::getIconTextFitPadding() const {
- return impl->layout.unevaluated.get<IconTextFitPadding>();
+ return impl().layout.get<IconTextFitPadding>();
}
void SymbolLayer::setIconTextFitPadding(PropertyValue<std::array<float, 4>> value) {
if (value == getIconTextFitPadding())
return;
- impl->layout.unevaluated.get<IconTextFitPadding>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-text-fit-padding");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconTextFitPadding>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<std::string> SymbolLayer::getDefaultIconImage() {
return IconImage::defaultValue();
}
DataDrivenPropertyValue<std::string> SymbolLayer::getIconImage() const {
- return impl->layout.unevaluated.get<IconImage>();
+ return impl().layout.get<IconImage>();
}
void SymbolLayer::setIconImage(DataDrivenPropertyValue<std::string> value) {
if (value == getIconImage())
return;
- impl->layout.unevaluated.get<IconImage>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-image");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconImage>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconRotate() {
return IconRotate::defaultValue();
}
DataDrivenPropertyValue<float> SymbolLayer::getIconRotate() const {
- return impl->layout.unevaluated.get<IconRotate>();
+ return impl().layout.get<IconRotate>();
}
void SymbolLayer::setIconRotate(DataDrivenPropertyValue<float> value) {
if (value == getIconRotate())
return;
- impl->layout.unevaluated.get<IconRotate>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-rotate");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconRotate>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<float> SymbolLayer::getDefaultIconPadding() {
return IconPadding::defaultValue();
}
PropertyValue<float> SymbolLayer::getIconPadding() const {
- return impl->layout.unevaluated.get<IconPadding>();
+ return impl().layout.get<IconPadding>();
}
void SymbolLayer::setIconPadding(PropertyValue<float> value) {
if (value == getIconPadding())
return;
- impl->layout.unevaluated.get<IconPadding>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-padding");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconPadding>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultIconKeepUpright() {
return IconKeepUpright::defaultValue();
}
PropertyValue<bool> SymbolLayer::getIconKeepUpright() const {
- return impl->layout.unevaluated.get<IconKeepUpright>();
+ return impl().layout.get<IconKeepUpright>();
}
void SymbolLayer::setIconKeepUpright(PropertyValue<bool> value) {
if (value == getIconKeepUpright())
return;
- impl->layout.unevaluated.get<IconKeepUpright>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-keep-upright");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconKeepUpright>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getDefaultIconOffset() {
return IconOffset::defaultValue();
}
DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getIconOffset() const {
- return impl->layout.unevaluated.get<IconOffset>();
+ return impl().layout.get<IconOffset>();
}
void SymbolLayer::setIconOffset(DataDrivenPropertyValue<std::array<float, 2>> value) {
if (value == getIconOffset())
return;
- impl->layout.unevaluated.get<IconOffset>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "icon-offset");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconOffset>() = value;
+ 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();
}
PropertyValue<AlignmentType> SymbolLayer::getTextPitchAlignment() const {
- return impl->layout.unevaluated.get<TextPitchAlignment>();
+ return impl().layout.get<TextPitchAlignment>();
}
void SymbolLayer::setTextPitchAlignment(PropertyValue<AlignmentType> value) {
if (value == getTextPitchAlignment())
return;
- impl->layout.unevaluated.get<TextPitchAlignment>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-pitch-alignment");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextPitchAlignment>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<AlignmentType> SymbolLayer::getDefaultTextRotationAlignment() {
return TextRotationAlignment::defaultValue();
}
PropertyValue<AlignmentType> SymbolLayer::getTextRotationAlignment() const {
- return impl->layout.unevaluated.get<TextRotationAlignment>();
+ return impl().layout.get<TextRotationAlignment>();
}
void SymbolLayer::setTextRotationAlignment(PropertyValue<AlignmentType> value) {
if (value == getTextRotationAlignment())
return;
- impl->layout.unevaluated.get<TextRotationAlignment>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-rotation-alignment");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextRotationAlignment>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<std::string> SymbolLayer::getDefaultTextField() {
return TextField::defaultValue();
}
DataDrivenPropertyValue<std::string> SymbolLayer::getTextField() const {
- return impl->layout.unevaluated.get<TextField>();
+ return impl().layout.get<TextField>();
}
void SymbolLayer::setTextField(DataDrivenPropertyValue<std::string> value) {
if (value == getTextField())
return;
- impl->layout.unevaluated.get<TextField>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-field");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextField>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<std::vector<std::string>> SymbolLayer::getDefaultTextFont() {
return TextFont::defaultValue();
}
PropertyValue<std::vector<std::string>> SymbolLayer::getTextFont() const {
- return impl->layout.unevaluated.get<TextFont>();
+ return impl().layout.get<TextFont>();
}
void SymbolLayer::setTextFont(PropertyValue<std::vector<std::string>> value) {
if (value == getTextFont())
return;
- impl->layout.unevaluated.get<TextFont>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-font");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextFont>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextSize() {
return TextSize::defaultValue();
}
DataDrivenPropertyValue<float> SymbolLayer::getTextSize() const {
- return impl->layout.unevaluated.get<TextSize>();
+ return impl().layout.get<TextSize>();
}
void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) {
if (value == getTextSize())
return;
- impl->layout.unevaluated.get<TextSize>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-size");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextSize>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
return TextMaxWidth::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextMaxWidth() const {
- return impl->layout.unevaluated.get<TextMaxWidth>();
+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;
- impl->layout.unevaluated.get<TextMaxWidth>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-max-width");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextMaxWidth>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<float> SymbolLayer::getDefaultTextLineHeight() {
return TextLineHeight::defaultValue();
}
PropertyValue<float> SymbolLayer::getTextLineHeight() const {
- return impl->layout.unevaluated.get<TextLineHeight>();
+ return impl().layout.get<TextLineHeight>();
}
void SymbolLayer::setTextLineHeight(PropertyValue<float> value) {
if (value == getTextLineHeight())
return;
- impl->layout.unevaluated.get<TextLineHeight>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-line-height");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextLineHeight>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
return TextLetterSpacing::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
- return impl->layout.unevaluated.get<TextLetterSpacing>();
+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;
- impl->layout.unevaluated.get<TextLetterSpacing>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-letter-spacing");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextLetterSpacing>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-PropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() {
+DataDrivenPropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() {
return TextJustify::defaultValue();
}
-PropertyValue<TextJustifyType> SymbolLayer::getTextJustify() const {
- return impl->layout.unevaluated.get<TextJustify>();
+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;
- impl->layout.unevaluated.get<TextJustify>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-justify");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextJustify>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-PropertyValue<TextAnchorType> SymbolLayer::getDefaultTextAnchor() {
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultTextAnchor() {
return TextAnchor::defaultValue();
}
-PropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const {
- return impl->layout.unevaluated.get<TextAnchor>();
+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;
- impl->layout.unevaluated.get<TextAnchor>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-anchor");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextAnchor>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<float> SymbolLayer::getDefaultTextMaxAngle() {
return TextMaxAngle::defaultValue();
}
PropertyValue<float> SymbolLayer::getTextMaxAngle() const {
- return impl->layout.unevaluated.get<TextMaxAngle>();
+ return impl().layout.get<TextMaxAngle>();
}
void SymbolLayer::setTextMaxAngle(PropertyValue<float> value) {
if (value == getTextMaxAngle())
return;
- impl->layout.unevaluated.get<TextMaxAngle>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-max-angle");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextMaxAngle>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextRotate() {
return TextRotate::defaultValue();
}
DataDrivenPropertyValue<float> SymbolLayer::getTextRotate() const {
- return impl->layout.unevaluated.get<TextRotate>();
+ return impl().layout.get<TextRotate>();
}
void SymbolLayer::setTextRotate(DataDrivenPropertyValue<float> value) {
if (value == getTextRotate())
return;
- impl->layout.unevaluated.get<TextRotate>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-rotate");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextRotate>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<float> SymbolLayer::getDefaultTextPadding() {
return TextPadding::defaultValue();
}
PropertyValue<float> SymbolLayer::getTextPadding() const {
- return impl->layout.unevaluated.get<TextPadding>();
+ return impl().layout.get<TextPadding>();
}
void SymbolLayer::setTextPadding(PropertyValue<float> value) {
if (value == getTextPadding())
return;
- impl->layout.unevaluated.get<TextPadding>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-padding");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextPadding>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultTextKeepUpright() {
return TextKeepUpright::defaultValue();
}
PropertyValue<bool> SymbolLayer::getTextKeepUpright() const {
- return impl->layout.unevaluated.get<TextKeepUpright>();
+ return impl().layout.get<TextKeepUpright>();
}
void SymbolLayer::setTextKeepUpright(PropertyValue<bool> value) {
if (value == getTextKeepUpright())
return;
- impl->layout.unevaluated.get<TextKeepUpright>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-keep-upright");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextKeepUpright>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<TextTransformType> SymbolLayer::getDefaultTextTransform() {
return TextTransform::defaultValue();
}
DataDrivenPropertyValue<TextTransformType> SymbolLayer::getTextTransform() const {
- return impl->layout.unevaluated.get<TextTransform>();
+ return impl().layout.get<TextTransform>();
}
void SymbolLayer::setTextTransform(DataDrivenPropertyValue<TextTransformType> value) {
if (value == getTextTransform())
return;
- impl->layout.unevaluated.get<TextTransform>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-transform");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextTransform>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getDefaultTextOffset() {
return TextOffset::defaultValue();
}
DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getTextOffset() const {
- return impl->layout.unevaluated.get<TextOffset>();
+ return impl().layout.get<TextOffset>();
}
void SymbolLayer::setTextOffset(DataDrivenPropertyValue<std::array<float, 2>> value) {
if (value == getTextOffset())
return;
- impl->layout.unevaluated.get<TextOffset>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-offset");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextOffset>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultTextAllowOverlap() {
return TextAllowOverlap::defaultValue();
}
PropertyValue<bool> SymbolLayer::getTextAllowOverlap() const {
- return impl->layout.unevaluated.get<TextAllowOverlap>();
+ return impl().layout.get<TextAllowOverlap>();
}
void SymbolLayer::setTextAllowOverlap(PropertyValue<bool> value) {
if (value == getTextAllowOverlap())
return;
- impl->layout.unevaluated.get<TextAllowOverlap>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-allow-overlap");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextAllowOverlap>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultTextIgnorePlacement() {
return TextIgnorePlacement::defaultValue();
}
PropertyValue<bool> SymbolLayer::getTextIgnorePlacement() const {
- return impl->layout.unevaluated.get<TextIgnorePlacement>();
+ return impl().layout.get<TextIgnorePlacement>();
}
void SymbolLayer::setTextIgnorePlacement(PropertyValue<bool> value) {
if (value == getTextIgnorePlacement())
return;
- impl->layout.unevaluated.get<TextIgnorePlacement>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-ignore-placement");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextIgnorePlacement>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
PropertyValue<bool> SymbolLayer::getDefaultTextOptional() {
return TextOptional::defaultValue();
}
PropertyValue<bool> SymbolLayer::getTextOptional() const {
- return impl->layout.unevaluated.get<TextOptional>();
+ return impl().layout.get<TextOptional>();
}
void SymbolLayer::setTextOptional(PropertyValue<bool> value) {
if (value == getTextOptional())
return;
- impl->layout.unevaluated.get<TextOptional>() = value;
- impl->observer->onLayerLayoutPropertyChanged(*this, "text-optional");
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextOptional>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Paint properties
@@ -546,362 +675,378 @@ DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconOpacity() {
return { 1 };
}
-DataDrivenPropertyValue<float> SymbolLayer::getIconOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconOpacity>().get(klass);
+DataDrivenPropertyValue<float> SymbolLayer::getIconOpacity() const {
+ return impl().paint.template get<IconOpacity>().value;
}
-void SymbolLayer::setIconOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getIconOpacity(klass))
+void SymbolLayer::setIconOpacity(DataDrivenPropertyValue<float> value) {
+ if (value == getIconOpacity())
return;
- impl->cascading.template get<IconOpacity>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setIconOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<IconOpacity>().setTransition(value, klass);
+void SymbolLayer::setIconOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getIconOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconOpacity>().getTransition(klass);
+TransitionOptions SymbolLayer::getIconOpacityTransition() const {
+ return impl().paint.template get<IconOpacity>().options;
}
DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconColor() {
return { Color::black() };
}
-DataDrivenPropertyValue<Color> SymbolLayer::getIconColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconColor>().get(klass);
+DataDrivenPropertyValue<Color> SymbolLayer::getIconColor() const {
+ return impl().paint.template get<IconColor>().value;
}
-void SymbolLayer::setIconColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getIconColor(klass))
+void SymbolLayer::setIconColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getIconColor())
return;
- impl->cascading.template get<IconColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setIconColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<IconColor>().setTransition(value, klass);
+void SymbolLayer::setIconColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getIconColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconColor>().getTransition(klass);
+TransitionOptions SymbolLayer::getIconColorTransition() const {
+ return impl().paint.template get<IconColor>().options;
}
DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconHaloColor() {
return { {} };
}
-DataDrivenPropertyValue<Color> SymbolLayer::getIconHaloColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconHaloColor>().get(klass);
+DataDrivenPropertyValue<Color> SymbolLayer::getIconHaloColor() const {
+ return impl().paint.template get<IconHaloColor>().value;
}
-void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getIconHaloColor(klass))
+void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getIconHaloColor())
return;
- impl->cascading.template get<IconHaloColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconHaloColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<IconHaloColor>().setTransition(value, klass);
+void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconHaloColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getIconHaloColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconHaloColor>().getTransition(klass);
+TransitionOptions SymbolLayer::getIconHaloColorTransition() const {
+ return impl().paint.template get<IconHaloColor>().options;
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloWidth() {
return { 0 };
}
-DataDrivenPropertyValue<float> SymbolLayer::getIconHaloWidth(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconHaloWidth>().get(klass);
+DataDrivenPropertyValue<float> SymbolLayer::getIconHaloWidth() const {
+ return impl().paint.template get<IconHaloWidth>().value;
}
-void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getIconHaloWidth(klass))
+void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue<float> value) {
+ if (value == getIconHaloWidth())
return;
- impl->cascading.template get<IconHaloWidth>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconHaloWidth>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<IconHaloWidth>().setTransition(value, klass);
+void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconHaloWidth>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getIconHaloWidthTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconHaloWidth>().getTransition(klass);
+TransitionOptions SymbolLayer::getIconHaloWidthTransition() const {
+ return impl().paint.template get<IconHaloWidth>().options;
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloBlur() {
return { 0 };
}
-DataDrivenPropertyValue<float> SymbolLayer::getIconHaloBlur(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconHaloBlur>().get(klass);
+DataDrivenPropertyValue<float> SymbolLayer::getIconHaloBlur() const {
+ return impl().paint.template get<IconHaloBlur>().value;
}
-void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getIconHaloBlur(klass))
+void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue<float> value) {
+ if (value == getIconHaloBlur())
return;
- impl->cascading.template get<IconHaloBlur>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconHaloBlur>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<IconHaloBlur>().setTransition(value, klass);
+void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconHaloBlur>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getIconHaloBlurTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconHaloBlur>().getTransition(klass);
+TransitionOptions SymbolLayer::getIconHaloBlurTransition() const {
+ return impl().paint.template get<IconHaloBlur>().options;
}
PropertyValue<std::array<float, 2>> SymbolLayer::getDefaultIconTranslate() {
return { {{ 0, 0 }} };
}
-PropertyValue<std::array<float, 2>> SymbolLayer::getIconTranslate(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconTranslate>().get(klass);
+PropertyValue<std::array<float, 2>> SymbolLayer::getIconTranslate() const {
+ return impl().paint.template get<IconTranslate>().value;
}
-void SymbolLayer::setIconTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
- if (value == getIconTranslate(klass))
+void SymbolLayer::setIconTranslate(PropertyValue<std::array<float, 2>> value) {
+ if (value == getIconTranslate())
return;
- impl->cascading.template get<IconTranslate>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconTranslate>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setIconTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<IconTranslate>().setTransition(value, klass);
+void SymbolLayer::setIconTranslateTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconTranslate>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getIconTranslateTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconTranslate>().getTransition(klass);
+TransitionOptions SymbolLayer::getIconTranslateTransition() const {
+ return impl().paint.template get<IconTranslate>().options;
}
PropertyValue<TranslateAnchorType> SymbolLayer::getDefaultIconTranslateAnchor() {
return { TranslateAnchorType::Map };
}
-PropertyValue<TranslateAnchorType> SymbolLayer::getIconTranslateAnchor(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconTranslateAnchor>().get(klass);
+PropertyValue<TranslateAnchorType> SymbolLayer::getIconTranslateAnchor() const {
+ return impl().paint.template get<IconTranslateAnchor>().value;
}
-void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
- if (value == getIconTranslateAnchor(klass))
+void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
+ if (value == getIconTranslateAnchor())
return;
- impl->cascading.template get<IconTranslateAnchor>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconTranslateAnchor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<IconTranslateAnchor>().setTransition(value, klass);
+void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<IconTranslateAnchor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getIconTranslateAnchorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<IconTranslateAnchor>().getTransition(klass);
+TransitionOptions SymbolLayer::getIconTranslateAnchorTransition() const {
+ return impl().paint.template get<IconTranslateAnchor>().options;
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextOpacity() {
return { 1 };
}
-DataDrivenPropertyValue<float> SymbolLayer::getTextOpacity(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextOpacity>().get(klass);
+DataDrivenPropertyValue<float> SymbolLayer::getTextOpacity() const {
+ return impl().paint.template get<TextOpacity>().value;
}
-void SymbolLayer::setTextOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getTextOpacity(klass))
+void SymbolLayer::setTextOpacity(DataDrivenPropertyValue<float> value) {
+ if (value == getTextOpacity())
return;
- impl->cascading.template get<TextOpacity>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextOpacity>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setTextOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<TextOpacity>().setTransition(value, klass);
+void SymbolLayer::setTextOpacityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextOpacity>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getTextOpacityTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextOpacity>().getTransition(klass);
+TransitionOptions SymbolLayer::getTextOpacityTransition() const {
+ return impl().paint.template get<TextOpacity>().options;
}
DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextColor() {
return { Color::black() };
}
-DataDrivenPropertyValue<Color> SymbolLayer::getTextColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextColor>().get(klass);
+DataDrivenPropertyValue<Color> SymbolLayer::getTextColor() const {
+ return impl().paint.template get<TextColor>().value;
}
-void SymbolLayer::setTextColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getTextColor(klass))
+void SymbolLayer::setTextColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getTextColor())
return;
- impl->cascading.template get<TextColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setTextColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<TextColor>().setTransition(value, klass);
+void SymbolLayer::setTextColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getTextColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextColor>().getTransition(klass);
+TransitionOptions SymbolLayer::getTextColorTransition() const {
+ return impl().paint.template get<TextColor>().options;
}
DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextHaloColor() {
return { {} };
}
-DataDrivenPropertyValue<Color> SymbolLayer::getTextHaloColor(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextHaloColor>().get(klass);
+DataDrivenPropertyValue<Color> SymbolLayer::getTextHaloColor() const {
+ return impl().paint.template get<TextHaloColor>().value;
}
-void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) {
- if (value == getTextHaloColor(klass))
+void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue<Color> value) {
+ if (value == getTextHaloColor())
return;
- impl->cascading.template get<TextHaloColor>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextHaloColor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<TextHaloColor>().setTransition(value, klass);
+void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextHaloColor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getTextHaloColorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextHaloColor>().getTransition(klass);
+TransitionOptions SymbolLayer::getTextHaloColorTransition() const {
+ return impl().paint.template get<TextHaloColor>().options;
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloWidth() {
return { 0 };
}
-DataDrivenPropertyValue<float> SymbolLayer::getTextHaloWidth(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextHaloWidth>().get(klass);
+DataDrivenPropertyValue<float> SymbolLayer::getTextHaloWidth() const {
+ return impl().paint.template get<TextHaloWidth>().value;
}
-void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getTextHaloWidth(klass))
+void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue<float> value) {
+ if (value == getTextHaloWidth())
return;
- impl->cascading.template get<TextHaloWidth>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextHaloWidth>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<TextHaloWidth>().setTransition(value, klass);
+void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextHaloWidth>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getTextHaloWidthTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextHaloWidth>().getTransition(klass);
+TransitionOptions SymbolLayer::getTextHaloWidthTransition() const {
+ return impl().paint.template get<TextHaloWidth>().options;
}
DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloBlur() {
return { 0 };
}
-DataDrivenPropertyValue<float> SymbolLayer::getTextHaloBlur(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextHaloBlur>().get(klass);
+DataDrivenPropertyValue<float> SymbolLayer::getTextHaloBlur() const {
+ return impl().paint.template get<TextHaloBlur>().value;
}
-void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) {
- if (value == getTextHaloBlur(klass))
+void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue<float> value) {
+ if (value == getTextHaloBlur())
return;
- impl->cascading.template get<TextHaloBlur>().set(value, klass);
- if (value.isDataDriven()) {
- impl->observer->onLayerDataDrivenPaintPropertyChanged(*this);
- } else {
- impl->observer->onLayerPaintPropertyChanged(*this);
- }
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextHaloBlur>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<TextHaloBlur>().setTransition(value, klass);
+void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextHaloBlur>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getTextHaloBlurTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextHaloBlur>().getTransition(klass);
+TransitionOptions SymbolLayer::getTextHaloBlurTransition() const {
+ return impl().paint.template get<TextHaloBlur>().options;
}
PropertyValue<std::array<float, 2>> SymbolLayer::getDefaultTextTranslate() {
return { {{ 0, 0 }} };
}
-PropertyValue<std::array<float, 2>> SymbolLayer::getTextTranslate(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextTranslate>().get(klass);
+PropertyValue<std::array<float, 2>> SymbolLayer::getTextTranslate() const {
+ return impl().paint.template get<TextTranslate>().value;
}
-void SymbolLayer::setTextTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
- if (value == getTextTranslate(klass))
+void SymbolLayer::setTextTranslate(PropertyValue<std::array<float, 2>> value) {
+ if (value == getTextTranslate())
return;
- impl->cascading.template get<TextTranslate>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextTranslate>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setTextTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<TextTranslate>().setTransition(value, klass);
+void SymbolLayer::setTextTranslateTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextTranslate>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getTextTranslateTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextTranslate>().getTransition(klass);
+TransitionOptions SymbolLayer::getTextTranslateTransition() const {
+ return impl().paint.template get<TextTranslate>().options;
}
PropertyValue<TranslateAnchorType> SymbolLayer::getDefaultTextTranslateAnchor() {
return { TranslateAnchorType::Map };
}
-PropertyValue<TranslateAnchorType> SymbolLayer::getTextTranslateAnchor(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextTranslateAnchor>().get(klass);
+PropertyValue<TranslateAnchorType> SymbolLayer::getTextTranslateAnchor() const {
+ return impl().paint.template get<TextTranslateAnchor>().value;
}
-void SymbolLayer::setTextTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
- if (value == getTextTranslateAnchor(klass))
+void SymbolLayer::setTextTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
+ if (value == getTextTranslateAnchor())
return;
- impl->cascading.template get<TextTranslateAnchor>().set(value, klass);
- impl->observer->onLayerPaintPropertyChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextTranslateAnchor>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
-void SymbolLayer::setTextTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) {
- impl->cascading.template get<TextTranslateAnchor>().setTransition(value, klass);
+void SymbolLayer::setTextTranslateAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<TextTranslateAnchor>().options = options;
+ baseImpl = std::move(impl_);
}
-TransitionOptions SymbolLayer::getTextTranslateAnchorTransition(const optional<std::string>& klass) const {
- return impl->cascading.template get<TextTranslateAnchor>().getTransition(klass);
+TransitionOptions SymbolLayer::getTextTranslateAnchorTransition() const {
+ return impl().paint.template get<TextTranslateAnchor>().options;
}
} // namespace style
diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp
index c99dd8ad70..b59768725d 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.cpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.cpp
@@ -1,11 +1,15 @@
#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
namespace mbgl {
namespace style {
-std::unique_ptr<RenderLayer> SymbolLayer::Impl::createRenderLayer() const {
- return std::make_unique<RenderSymbolLayer>(*this);
+bool SymbolLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
+ assert(dynamic_cast<const SymbolLayer::Impl*>(&other));
+ const auto& impl = static_cast<const style::SymbolLayer::Impl&>(other);
+ return filter != impl.filter ||
+ visibility != impl.visibility ||
+ layout != impl.layout ||
+ paint.hasDataDrivenPropertyDifference(impl.paint);
}
} // namespace style
diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp
index df145647a0..f8ef87dcdf 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.hpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.hpp
@@ -1,24 +1,21 @@
#pragma once
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
namespace mbgl {
-
namespace style {
class SymbolLayer::Impl : public Layer::Impl {
public:
- std::unique_ptr<Layer> clone() const override;
- std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
- void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
+ using Layer::Impl::Impl;
- std::unique_ptr<RenderLayer> createRenderLayer() const override;
+ bool hasLayoutDifference(const Layer::Impl&) const override;
+ void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- SymbolLayoutProperties layout;
- SymbolPaintProperties::Cascading cascading;
+ SymbolLayoutProperties::Unevaluated layout;
+ SymbolPaintProperties::Transitionable paint;
};
} // namespace style
diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp
index 5b57175785..436b5cbd4f 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
@@ -86,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; }
@@ -111,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; }
};
@@ -121,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> {
@@ -237,7 +248,7 @@ struct TextTranslateAnchor : PaintProperty<TranslateAnchorType> {
static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; }
};
-class SymbolLayoutProperties : public LayoutProperties<
+class SymbolLayoutProperties : public Properties<
SymbolPlacement,
SymbolSpacing,
SymbolAvoidEdges,
@@ -253,6 +264,8 @@ class SymbolLayoutProperties : public LayoutProperties<
IconPadding,
IconKeepUpright,
IconOffset,
+ IconAnchor,
+ IconPitchAlignment,
TextPitchAlignment,
TextRotationAlignment,
TextField,
@@ -274,7 +287,7 @@ class SymbolLayoutProperties : public LayoutProperties<
TextOptional
> {};
-class SymbolPaintProperties : public PaintProperties<
+class SymbolPaintProperties : public Properties<
IconOpacity,
IconColor,
IconHaloColor,
diff --git a/src/mbgl/style/layout_property.hpp b/src/mbgl/style/layout_property.hpp
index 3b9d6114c0..8c59295ad2 100644
--- a/src/mbgl/style/layout_property.hpp
+++ b/src/mbgl/style/layout_property.hpp
@@ -4,117 +4,30 @@
#include <mbgl/style/data_driven_property_value.hpp>
#include <mbgl/renderer/property_evaluator.hpp>
#include <mbgl/renderer/data_driven_property_evaluator.hpp>
-#include <mbgl/util/indexed_tuple.hpp>
namespace mbgl {
-
-class PropertyEvaluationParameters;
-
namespace style {
template <class T>
class LayoutProperty {
public:
+ using TransitionableType = std::nullptr_t;
using UnevaluatedType = PropertyValue<T>;
using EvaluatorType = PropertyEvaluator<T>;
using PossiblyEvaluatedType = T;
using Type = T;
+ static constexpr bool IsDataDriven = false;
};
template <class T>
class DataDrivenLayoutProperty {
public:
+ using TransitionableType = std::nullptr_t;
using UnevaluatedType = DataDrivenPropertyValue<T>;
using EvaluatorType = DataDrivenPropertyEvaluator<T>;
using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue<T>;
using Type = T;
-};
-
-template <class... Ps>
-class LayoutProperties {
-public:
- using Properties = TypeList<Ps...>;
-
- template <class TypeList>
- using Tuple = IndexedTuple<Properties, TypeList>;
-
- /*
- For layout properties we implement a two step evaluation process: if you have a zoom level,
- you can evaluate a set of unevaluated property values, producing a set of possibly evaluated
- values, where undefined, constant, or camera function values have been fully evaluated, and
- source or composite function values have not.
-
- Once you also have a particular feature, you can evaluate that set of possibly evaluated values
- fully, producing a set of fully evaluated values.
-
- This is in theory maximally efficient in terms of avoiding repeated evaluation of camera
- functions, though it's more of a historical accident than a purposeful optimization.
- */
-
- using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>;
- using PossiblyEvaluatedTypes = TypeList<typename Ps::PossiblyEvaluatedType...>;
- using EvaluatedTypes = TypeList<typename Ps::Type...>;
-
- class Evaluated : public Tuple<EvaluatedTypes> {
- public:
- using Tuple<EvaluatedTypes>::Tuple;
- };
-
- class PossiblyEvaluated : public Tuple<PossiblyEvaluatedTypes> {
- public:
- using Tuple<PossiblyEvaluatedTypes>::Tuple;
-
- template <class T>
- static T evaluate(float, const GeometryTileFeature&, const T& t, const T&) {
- return t;
- }
-
- template <class T>
- static T evaluate(float z, const GeometryTileFeature& feature,
- const PossiblyEvaluatedPropertyValue<T>& v, const T& defaultValue) {
- return v.match(
- [&] (const T& t) {
- return t;
- },
- [&] (const SourceFunction<T>& t) {
- return t.evaluate(feature, defaultValue);
- },
- [&] (const CompositeFunction<T>& t) {
- return t.evaluate(z, feature, defaultValue);
- });
- }
-
- template <class P>
- auto evaluate(float z, const GeometryTileFeature& feature) const {
- return evaluate(z, feature, this->template get<P>(), P::defaultValue());
- }
-
- Evaluated evaluate(float z, const GeometryTileFeature& feature) const {
- return Evaluated {
- evaluate<Ps>(z, feature)...
- };
- }
- };
-
- class Unevaluated : public Tuple<UnevaluatedTypes> {
- public:
- using Tuple<UnevaluatedTypes>::Tuple;
- };
-
- template <class P>
- auto evaluate(const PropertyEvaluationParameters& parameters) const {
- using Evaluator = typename P::EvaluatorType;
- return unevaluated.template get<P>()
- .evaluate(Evaluator(parameters, P::defaultValue()));
- }
-
- PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters) const {
- return PossiblyEvaluated {
- evaluate<Ps>(parameters)...
- };
- }
-
- Unevaluated unevaluated;
+ static constexpr bool IsDataDriven = true;
};
} // namespace style
diff --git a/src/mbgl/style/light.cpp b/src/mbgl/style/light.cpp
index b54920713c..352dc4d942 100644
--- a/src/mbgl/style/light.cpp
+++ b/src/mbgl/style/light.cpp
@@ -2,17 +2,28 @@
#include <mbgl/style/light.hpp>
#include <mbgl/style/light_impl.hpp>
-#include <mbgl/style/light_properties.hpp>
+#include <mbgl/style/light_observer.hpp>
namespace mbgl {
namespace style {
+static LightObserver nullObserver;
+
Light::Light()
- : impl(std::make_unique<Impl>()) {
+ : impl(makeMutable<Impl>()),
+ observer(&nullObserver) {
}
Light::~Light() = default;
+void Light::setObserver(LightObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver;
+}
+
+Mutable<Light::Impl> Light::mutableImpl() const {
+ return makeMutable<Impl>(*impl);
+}
+
LightAnchorType Light::getDefaultAnchor() {
return LightAnchor::defaultValue();
}
@@ -22,17 +33,21 @@ PropertyValue<LightAnchorType> Light::getAnchor() const {
}
void Light::setAnchor(PropertyValue<LightAnchorType> property) {
- impl->properties.template get<LightAnchor>().value = property;
- impl->observer->onLightChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightAnchor>().value = property;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
-void Light::setAnchorTransition(const TransitionOptions& transition) {
- impl->properties.template get<LightAnchor>().transition = transition;
- impl->observer->onLightChanged(*this);
+void Light::setAnchorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightAnchor>().options = options;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
TransitionOptions Light::getAnchorTransition() const {
- return impl->properties.template get<LightAnchor>().transition;
+ return impl->properties.template get<LightAnchor>().options;
}
Position Light::getDefaultPosition() {
@@ -44,17 +59,21 @@ PropertyValue<Position> Light::getPosition() const {
}
void Light::setPosition(PropertyValue<Position> property) {
- impl->properties.template get<LightPosition>().value = property;
- impl->observer->onLightChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightPosition>().value = property;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
-void Light::setPositionTransition(const TransitionOptions& transition) {
- impl->properties.template get<LightPosition>().transition = transition;
- impl->observer->onLightChanged(*this);
+void Light::setPositionTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightPosition>().options = options;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
TransitionOptions Light::getPositionTransition() const {
- return impl->properties.template get<LightPosition>().transition;
+ return impl->properties.template get<LightPosition>().options;
}
Color Light::getDefaultColor() {
@@ -66,17 +85,21 @@ PropertyValue<Color> Light::getColor() const {
}
void Light::setColor(PropertyValue<Color> property) {
- impl->properties.template get<LightColor>().value = property;
- impl->observer->onLightChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightColor>().value = property;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
-void Light::setColorTransition(const TransitionOptions& transition) {
- impl->properties.template get<LightColor>().transition = transition;
- impl->observer->onLightChanged(*this);
+void Light::setColorTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightColor>().options = options;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
TransitionOptions Light::getColorTransition() const {
- return impl->properties.template get<LightColor>().transition;
+ return impl->properties.template get<LightColor>().options;
}
float Light::getDefaultIntensity() {
@@ -88,17 +111,21 @@ PropertyValue<float> Light::getIntensity() const {
}
void Light::setIntensity(PropertyValue<float> property) {
- impl->properties.template get<LightIntensity>().value = property;
- impl->observer->onLightChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightIntensity>().value = property;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
-void Light::setIntensityTransition(const TransitionOptions& transition) {
- impl->properties.template get<LightIntensity>().transition = transition;
- impl->observer->onLightChanged(*this);
+void Light::setIntensityTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<LightIntensity>().options = options;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
TransitionOptions Light::getIntensityTransition() const {
- return impl->properties.template get<LightIntensity>().transition;
+ return impl->properties.template get<LightIntensity>().options;
}
diff --git a/src/mbgl/style/light.cpp.ejs b/src/mbgl/style/light.cpp.ejs
index c82c65c10c..45241c60fd 100644
--- a/src/mbgl/style/light.cpp.ejs
+++ b/src/mbgl/style/light.cpp.ejs
@@ -5,17 +5,28 @@
#include <mbgl/style/light.hpp>
#include <mbgl/style/light_impl.hpp>
-#include <mbgl/style/light_properties.hpp>
+#include <mbgl/style/light_observer.hpp>
namespace mbgl {
namespace style {
+static LightObserver nullObserver;
+
Light::Light()
- : impl(std::make_unique<Impl>()) {
+ : impl(makeMutable<Impl>()),
+ observer(&nullObserver) {
}
Light::~Light() = default;
+void Light::setObserver(LightObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver;
+}
+
+Mutable<Light::Impl> Light::mutableImpl() const {
+ return makeMutable<Impl>(*impl);
+}
+
<% for (const property of properties) { -%>
<%- evaluatedType(property) %> Light::getDefault<%- camelize(property.name) %>() {
return Light<%- camelize(property.name) %>::defaultValue();
@@ -26,17 +37,21 @@ Light::~Light() = default;
}
void Light::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> property) {
- impl->properties.template get<Light<%- camelize(property.name) %>>().value = property;
- impl->observer->onLightChanged(*this);
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<Light<%- camelize(property.name) %>>().value = property;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
-void Light::set<%- camelize(property.name) %>Transition(const TransitionOptions& transition) {
- impl->properties.template get<Light<%- camelize(property.name) %>>().transition = transition;
- impl->observer->onLightChanged(*this);
+void Light::set<%- camelize(property.name) %>Transition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->properties.template get<Light<%- camelize(property.name) %>>().options = options;
+ impl = std::move(impl_);
+ observer->onLightChanged(*this);
}
TransitionOptions Light::get<%- camelize(property.name) %>Transition() const {
- return impl->properties.template get<Light<%- camelize(property.name) %>>().transition;
+ return impl->properties.template get<Light<%- camelize(property.name) %>>().options;
}
<% } -%>
diff --git a/src/mbgl/style/light_impl.cpp b/src/mbgl/style/light_impl.cpp
index e0ab1176ed..619d115f02 100644
--- a/src/mbgl/style/light_impl.cpp
+++ b/src/mbgl/style/light_impl.cpp
@@ -3,9 +3,5 @@
namespace mbgl {
namespace style {
-void Light::Impl::setObserver(LightObserver* observer_) {
- observer = observer_;
-}
-
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/light_impl.hpp b/src/mbgl/style/light_impl.hpp
index b4fd886742..f094c9d462 100644
--- a/src/mbgl/style/light_impl.hpp
+++ b/src/mbgl/style/light_impl.hpp
@@ -1,19 +1,58 @@
#pragma once
-#include <mbgl/style/light_properties.hpp>
-#include <mbgl/style/light_observer.hpp>
+#include <mbgl/style/light.hpp>
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/style/properties.hpp>
+#include <mbgl/renderer/property_evaluator.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/util/indexed_tuple.hpp>
namespace mbgl {
namespace style {
-class Light::Impl {
+template <class T>
+class LightProperty {
public:
+ using TransitionableType = Transitionable<PropertyValue<T>>;
+ using UnevaluatedType = Transitioning<PropertyValue<T>>;
+ using EvaluatorType = PropertyEvaluator<T>;
+ using PossiblyEvaluatedType = T;
+ using Type = T;
+ static constexpr bool IsDataDriven = false;
+};
+
+struct LightAnchor : LightProperty<LightAnchorType> {
+ static LightAnchorType defaultValue() {
+ return LightAnchorType::Viewport;
+ }
+};
+
+struct LightPosition : LightProperty<Position> {
+ static Position defaultValue() {
+ std::array<float, 3> default_ = { { 1.15, 210, 30 } };
+ return Position{ { default_ } };
+ }
+};
- LightObserver nullObserver;
- LightObserver* observer = &nullObserver;
- void setObserver(LightObserver*);
+struct LightColor : LightProperty<Color> {
+ static Color defaultValue() {
+ return Color::white();
+ }
+};
+
+struct LightIntensity : LightProperty<float> {
+ static float defaultValue() {
+ return 0.5;
+ }
+};
- IndexedTuple<LightProperties, LightProperties> properties;
+using LightProperties = Properties<LightAnchor, LightPosition, LightColor, LightIntensity>;
+
+class Light::Impl {
+public:
+ LightProperties::Transitionable properties;
};
} // namespace style
diff --git a/src/mbgl/style/light_observer.hpp b/src/mbgl/style/light_observer.hpp
index 751a84850d..45beb64928 100644
--- a/src/mbgl/style/light_observer.hpp
+++ b/src/mbgl/style/light_observer.hpp
@@ -1,10 +1,10 @@
#pragma once
-#include <mbgl/style/light.hpp>
-
namespace mbgl {
namespace style {
+class Light;
+
class LightObserver {
public:
virtual ~LightObserver() = default;
diff --git a/src/mbgl/style/light_properties.hpp b/src/mbgl/style/light_properties.hpp
deleted file mode 100644
index 9f6088a633..0000000000
--- a/src/mbgl/style/light_properties.hpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#pragma once
-
-#include <mbgl/style/property_value.hpp>
-#include <mbgl/style/transition_options.hpp>
-#include <mbgl/style/types.hpp>
-#include <mbgl/style/position.hpp>
-#include <mbgl/util/color.hpp>
-#include <mbgl/util/indexed_tuple.hpp>
-
-namespace mbgl {
-namespace style {
-
-template <class T>
-class LightProperty {
-public:
- using Type = T;
- using ValueType = PropertyValue<T>;
-
- PropertyValue<T> value;
- TransitionOptions transition;
-};
-
-struct LightAnchor : LightProperty<LightAnchorType> {
- static LightAnchorType defaultValue() {
- return LightAnchorType::Viewport;
- }
-};
-
-struct LightPosition : LightProperty<Position> {
- static Position defaultValue() {
- std::array<float, 3> default_ = { { 1.15, 210, 30 } };
- return Position{ { default_ } };
- }
-};
-
-struct LightColor : LightProperty<Color> {
- static Color defaultValue() {
- return Color::white();
- }
-};
-
-struct LightIntensity : LightProperty<float> {
- static float defaultValue() {
- return 0.5;
- }
-};
-
-using LightProperties = TypeList<LightAnchor, LightPosition, LightColor, LightIntensity>;
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp
index 60432334e2..cc6378b366 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -1,22 +1,18 @@
#pragma once
-#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/sprite/sprite_atlas_observer.hpp>
#include <mbgl/style/source_observer.hpp>
-#include <mbgl/map/update.hpp>
#include <exception>
namespace mbgl {
namespace style {
-class Observer : public GlyphAtlasObserver,
- public SpriteAtlasObserver,
- public SourceObserver {
+class Observer : public SourceObserver {
public:
- virtual void onUpdate(Update) {}
- virtual void onStyleError(std::exception_ptr) {}
+ virtual void onStyleLoading() {}
virtual void onStyleLoaded() {}
+ virtual void onUpdate() {}
+ virtual void onStyleError(std::exception_ptr) {}
virtual void onResourceError(std::exception_ptr) {}
};
diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp
index c203083c49..c4c996b3bd 100644
--- a/src/mbgl/style/paint_property.hpp
+++ b/src/mbgl/style/paint_property.hpp
@@ -1,108 +1,38 @@
#pragma once
-#include <mbgl/style/class_dictionary.hpp>
+#include <mbgl/style/properties.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
-#include <mbgl/style/transition_options.hpp>
#include <mbgl/renderer/property_evaluator.hpp>
#include <mbgl/renderer/cross_faded_property_evaluator.hpp>
#include <mbgl/renderer/data_driven_property_evaluator.hpp>
-#include <mbgl/renderer/property_evaluation_parameters.hpp>
-#include <mbgl/renderer/cascade_parameters.hpp>
-#include <mbgl/renderer/transitioning_property.hpp>
-#include <mbgl/renderer/paint_property_binder.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/interpolate.hpp>
-#include <mbgl/util/indexed_tuple.hpp>
-#include <mbgl/util/ignore.hpp>
#include <utility>
namespace mbgl {
-
-class GeometryTileFeature;
-
namespace style {
-template <class Value>
-class CascadingPaintProperty {
-public:
- bool isUndefined() const {
- return values.find(ClassID::Default) == values.end();
- }
-
- const Value& get(const optional<std::string>& klass) const {
- static const Value staticValue{};
- const auto it = values.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default);
- return it == values.end() ? staticValue : it->second;
- }
-
- void set(const Value& value_, const optional<std::string>& klass) {
- values[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = value_;
- }
-
- const TransitionOptions& getTransition(const optional<std::string>& klass) const {
- static const TransitionOptions staticValue{};
- const auto it = transitions.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default);
- return it == transitions.end() ? staticValue : it->second;
- }
-
- void setTransition(const TransitionOptions& transition, const optional<std::string>& klass) {
- transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition;
- }
-
- template <class TransitioningProperty>
- TransitioningProperty cascade(const CascadeParameters& params, TransitioningProperty prior) const {
- TransitionOptions transition;
- Value value;
-
- for (const auto classID : params.classes) {
- if (values.find(classID) != values.end()) {
- value = values.at(classID);
- break;
- }
- }
-
- for (const auto classID : params.classes) {
- if (transitions.find(classID) != transitions.end()) {
- transition = transitions.at(classID).reverseMerge(transition);
- break;
- }
- }
-
- return TransitioningProperty(std::move(value),
- std::move(prior),
- transition.reverseMerge(params.transition),
- params.now);
- }
-
-private:
- std::map<ClassID, Value> values;
- std::map<ClassID, TransitionOptions> transitions;
-};
-
template <class T>
class PaintProperty {
public:
- using ValueType = PropertyValue<T>;
- using CascadingType = CascadingPaintProperty<ValueType>;
- using UnevaluatedType = TransitioningProperty<ValueType>;
+ using TransitionableType = Transitionable<PropertyValue<T>>;
+ using UnevaluatedType = Transitioning<PropertyValue<T>>;
using EvaluatorType = PropertyEvaluator<T>;
- using EvaluatedType = T;
+ using PossiblyEvaluatedType = T;
+ using Type = T;
static constexpr bool IsDataDriven = false;
};
template <class T, class A, class U>
class DataDrivenPaintProperty {
public:
- using ValueType = DataDrivenPropertyValue<T>;
- using CascadingType = CascadingPaintProperty<ValueType>;
- using UnevaluatedType = TransitioningProperty<ValueType>;
+ using TransitionableType = Transitionable<DataDrivenPropertyValue<T>>;
+ using UnevaluatedType = Transitioning<DataDrivenPropertyValue<T>>;
using EvaluatorType = DataDrivenPropertyEvaluator<T>;
- using EvaluatedType = PossiblyEvaluatedPropertyValue<T>;
+ using PossiblyEvaluatedType = PossiblyEvaluatedPropertyValue<T>;
+ using Type = T;
static constexpr bool IsDataDriven = true;
- using Type = T;
using Attribute = A;
using Uniform = U;
};
@@ -110,78 +40,13 @@ public:
template <class T>
class CrossFadedPaintProperty {
public:
- using ValueType = PropertyValue<T>;
- using CascadingType = CascadingPaintProperty<ValueType>;
- using UnevaluatedType = TransitioningProperty<ValueType>;
+ using TransitionableType = Transitionable<PropertyValue<T>>;
+ using UnevaluatedType = Transitioning<PropertyValue<T>>;
using EvaluatorType = CrossFadedPropertyEvaluator<T>;
- using EvaluatedType = Faded<T>;
+ using PossiblyEvaluatedType = Faded<T>;
+ using Type = T;
static constexpr bool IsDataDriven = false;
};
-template <class P>
-struct IsDataDriven : std::integral_constant<bool, P::IsDataDriven> {};
-
-template <class... Ps>
-class PaintProperties {
-public:
- using Properties = TypeList<Ps...>;
- using DataDrivenProperties = FilteredTypeList<Properties, IsDataDriven>;
- using Binders = PaintPropertyBinders<DataDrivenProperties>;
-
- using EvaluatedTypes = TypeList<typename Ps::EvaluatedType...>;
- using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>;
- using CascadingTypes = TypeList<typename Ps::CascadingType...>;
-
- template <class TypeList>
- using Tuple = IndexedTuple<Properties, TypeList>;
-
- class Evaluated : public Tuple<EvaluatedTypes> {
- public:
- using Tuple<EvaluatedTypes>::Tuple;
- };
-
- class Unevaluated : public Tuple<UnevaluatedTypes> {
- public:
- using Tuple<UnevaluatedTypes>::Tuple;
-
- bool hasTransition() const {
- bool result = false;
- util::ignore({ result |= this->template get<Ps>().hasTransition()... });
- return result;
- }
-
- template <class P>
- auto evaluate(const PropertyEvaluationParameters& parameters) {
- using Evaluator = typename P::EvaluatorType;
-
- return this->template get<P>().evaluate(
- Evaluator(parameters, P::defaultValue()),
- parameters.now
- );
- }
-
- Evaluated evaluate(const PropertyEvaluationParameters& parameters) {
- return Evaluated {
- evaluate<Ps>(parameters)...
- };
- }
-
- };
-
- class Cascading : public Tuple<CascadingTypes> {
- public:
- using Tuple<CascadingTypes>::Tuple;
-
- Unevaluated cascade(const CascadeParameters& parameters, Unevaluated&& prior) const {
- return Unevaluated {
- this->template get<Ps>().cascade(
- parameters,
- std::move(prior.template get<Ps>())
- )...
- };
- }
- };
-};
-
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp
index fc3ccf410b..a83897dbf5 100644
--- a/src/mbgl/style/parser.cpp
+++ b/src/mbgl/style/parser.cpp
@@ -2,11 +2,13 @@
#include <mbgl/style/layer_impl.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/util/logging.hpp>
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
@@ -55,10 +57,12 @@ StyleParseResult Parser::parse(const std::string& json) {
if (document.HasMember("center")) {
const JSValue& value = document["center"];
- if (value.IsArray() && value.Size() >= 2) {
- // Style spec uses lon/lat order
- latLng = LatLng(value[1].IsNumber() ? value[1].GetDouble() : 0,
- value[0].IsNumber() ? value[0].GetDouble() : 0);
+ conversion::Error error;
+ auto convertedLatLng = conversion::convert<LatLng>(value, error);
+ if (convertedLatLng) {
+ latLng = *convertedLatLng;
+ } else {
+ Log::Warning(Event::ParseStyle, "center coordinate must be a longitude, latitude pair");
}
}
@@ -83,6 +87,10 @@ StyleParseResult Parser::parse(const std::string& json) {
}
}
+ if (document.HasMember("transition")) {
+ parseTransition(document["transition"]);
+ }
+
if (document.HasMember("light")) {
parseLight(document["light"]);
}
@@ -112,6 +120,17 @@ StyleParseResult Parser::parse(const std::string& json) {
return nullptr;
}
+void Parser::parseTransition(const JSValue& value) {
+ conversion::Error error;
+ optional<TransitionOptions> converted = conversion::convert<TransitionOptions>(value, error);
+ if (!converted) {
+ Log::Warning(Event::ParseStyle, error.message);
+ return;
+ }
+
+ transition = std::move(*converted);
+}
+
void Parser::parseLight(const JSValue& value) {
conversion::Error error;
optional<Light> converted = conversion::convert<Light>(value, error);
@@ -236,7 +255,7 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique
return;
}
- layer = reference->baseImpl->cloneRef(id);
+ layer = reference->cloneRef(id);
conversion::setPaintProperties(*layer, value);
} else {
conversion::Error error;
diff --git a/src/mbgl/style/parser.hpp b/src/mbgl/style/parser.hpp
index 32b8a7a8bc..401b5ff513 100644
--- a/src/mbgl/style/parser.hpp
+++ b/src/mbgl/style/parser.hpp
@@ -32,6 +32,7 @@ public:
std::vector<std::unique_ptr<Source>> sources;
std::vector<std::unique_ptr<Layer>> layers;
+ TransitionOptions transition;
Light light;
std::string name;
@@ -44,6 +45,7 @@ public:
std::vector<FontStack> fontStacks() const;
private:
+ void parseTransition(const JSValue&);
void parseLight(const JSValue&);
void parseSources(const JSValue&);
void parseLayers(const JSValue&);
diff --git a/src/mbgl/style/properties.hpp b/src/mbgl/style/properties.hpp
new file mode 100644
index 0000000000..dfcf7993a7
--- /dev/null
+++ b/src/mbgl/style/properties.hpp
@@ -0,0 +1,248 @@
+#pragma once
+
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/conversion/stringify.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/renderer/paint_property_binder.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/util/indexed_tuple.hpp>
+#include <mbgl/util/ignore.hpp>
+
+namespace mbgl {
+
+class GeometryTileFeature;
+
+namespace style {
+
+template <class Value>
+class Transitioning {
+public:
+ Transitioning() = default;
+
+ explicit Transitioning(Value value_)
+ : value(std::move(value_)) {
+ }
+
+ Transitioning(Value value_,
+ Transitioning<Value> prior_,
+ TransitionOptions transition,
+ TimePoint now)
+ : begin(now + transition.delay.value_or(Duration::zero())),
+ end(begin + transition.duration.value_or(Duration::zero())),
+ value(std::move(value_)) {
+ if (transition.isDefined()) {
+ prior = { std::move(prior_) };
+ }
+ }
+
+ template <class Evaluator>
+ auto evaluate(const Evaluator& evaluator, TimePoint now) const {
+ auto finalValue = value.evaluate(evaluator);
+ if (!prior) {
+ // No prior value.
+ return finalValue;
+ } else if (now >= end) {
+ // Transition from prior value is now complete.
+ prior = {};
+ return finalValue;
+ } else if (value.isDataDriven()) {
+ // Transitions to data-driven properties are not supported.
+ // We snap immediately to the data-driven value so that, when we perform layout,
+ // we see the data-driven function and can use it to populate vertex buffers.
+ prior = {};
+ return finalValue;
+ } else if (now < begin) {
+ // Transition hasn't started yet.
+ return prior->get().evaluate(evaluator, now);
+ } else {
+ // Interpolate between recursively-calculated prior value and final.
+ float t = std::chrono::duration<float>(now - begin) / (end - begin);
+ return util::interpolate(prior->get().evaluate(evaluator, now), finalValue,
+ util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
+ }
+ }
+
+ bool hasTransition() const {
+ return bool(prior);
+ }
+
+ bool isUndefined() const {
+ return value.isUndefined();
+ }
+
+ const Value& getValue() const {
+ return value;
+ }
+
+private:
+ mutable optional<mapbox::util::recursive_wrapper<Transitioning<Value>>> prior;
+ TimePoint begin;
+ TimePoint end;
+ Value value;
+};
+
+template <class Value>
+class Transitionable {
+public:
+ Value value;
+ TransitionOptions options;
+
+ Transitioning<Value> transition(const TransitionParameters& params, Transitioning<Value> prior) const {
+ return Transitioning<Value>(value,
+ std::move(prior),
+ options.reverseMerge(params.transition),
+ params.now);
+ }
+};
+
+template <class P>
+struct IsDataDriven : std::integral_constant<bool, P::IsDataDriven> {};
+
+template <class... Ps>
+class Properties {
+public:
+ /*
+ For style properties we implement a two step evaluation process: if you have a zoom level,
+ you can evaluate a set of unevaluated property values, producing a set of possibly evaluated
+ values, where undefined, constant, or camera function values have been fully evaluated, and
+ source or composite function values have not.
+
+ Once you also have a particular feature, you can evaluate that set of possibly evaluated values
+ fully, producing a set of fully evaluated values.
+
+ This is in theory maximally efficient in terms of avoiding repeated evaluation of camera
+ functions, though it's more of a historical accident than a purposeful optimization.
+ */
+
+ using PropertyTypes = TypeList<Ps...>;
+ using TransitionableTypes = TypeList<typename Ps::TransitionableType...>;
+ using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>;
+ using PossiblyEvaluatedTypes = TypeList<typename Ps::PossiblyEvaluatedType...>;
+ using EvaluatedTypes = TypeList<typename Ps::Type...>;
+
+ using DataDrivenProperties = FilteredTypeList<PropertyTypes, IsDataDriven>;
+ using Binders = PaintPropertyBinders<DataDrivenProperties>;
+
+ template <class TypeList>
+ using Tuple = IndexedTuple<PropertyTypes, TypeList>;
+
+ class Evaluated : public Tuple<EvaluatedTypes> {
+ public:
+ template <class... Us>
+ Evaluated(Us&&... us)
+ : Tuple<EvaluatedTypes>(std::forward<Us>(us)...) {
+ }
+ };
+
+ class PossiblyEvaluated : public Tuple<PossiblyEvaluatedTypes> {
+ public:
+ template <class... Us>
+ PossiblyEvaluated(Us&&... us)
+ : Tuple<PossiblyEvaluatedTypes>(std::forward<Us>(us)...) {
+ }
+
+ template <class T>
+ static T evaluate(float, const GeometryTileFeature&, const T& t, const T&) {
+ return t;
+ }
+
+ template <class T>
+ static T evaluate(float z, const GeometryTileFeature& feature,
+ const PossiblyEvaluatedPropertyValue<T>& v, const T& defaultValue) {
+ return v.match(
+ [&] (const T& t) {
+ return t;
+ },
+ [&] (const SourceFunction<T>& t) {
+ return t.evaluate(feature, defaultValue);
+ },
+ [&] (const CompositeFunction<T>& t) {
+ return t.evaluate(z, feature, defaultValue);
+ });
+ }
+
+ template <class P>
+ auto evaluate(float z, const GeometryTileFeature& feature) const {
+ return evaluate(z, feature, this->template get<P>(), P::defaultValue());
+ }
+
+ Evaluated evaluate(float z, const GeometryTileFeature& feature) const {
+ return Evaluated {
+ evaluate<Ps>(z, feature)...
+ };
+ }
+ };
+
+ class Unevaluated : public Tuple<UnevaluatedTypes> {
+ public:
+ template <class... Us>
+ Unevaluated(Us&&... us)
+ : Tuple<UnevaluatedTypes>(std::forward<Us>(us)...) {
+ }
+
+ bool hasTransition() const {
+ bool result = false;
+ util::ignore({ result |= this->template get<Ps>().hasTransition()... });
+ return result;
+ }
+
+ template <class P>
+ auto evaluate(const PropertyEvaluationParameters& parameters) const {
+ using Evaluator = typename P::EvaluatorType;
+ return this->template get<P>()
+ .evaluate(Evaluator(parameters, P::defaultValue()), parameters.now);
+ }
+
+ PossiblyEvaluated evaluate(const PropertyEvaluationParameters& parameters) const {
+ return PossiblyEvaluated {
+ evaluate<Ps>(parameters)...
+ };
+ }
+
+ template <class Writer>
+ void stringify(Writer& writer) const {
+ writer.StartObject();
+ util::ignore({ (conversion::stringify<Ps>(writer, this->template get<Ps>()), 0)... });
+ writer.EndObject();
+ }
+ };
+
+ class Transitionable : public Tuple<TransitionableTypes> {
+ public:
+ template <class... Us>
+ Transitionable(Us&&... us)
+ : Tuple<TransitionableTypes>(std::forward<Us>(us)...) {
+ }
+
+ Unevaluated transitioned(const TransitionParameters& parameters, Unevaluated&& prior) const {
+ return Unevaluated {
+ this->template get<Ps>()
+ .transition(parameters, std::move(prior.template get<Ps>()))...
+ };
+ }
+
+ Unevaluated untransitioned() const {
+ return Unevaluated {
+ typename Ps::UnevaluatedType(this->template get<Ps>().value)...
+ };
+ }
+
+ bool hasDataDrivenPropertyDifference(const Transitionable& other) const {
+ bool result = false;
+ util::ignore({ (result |= this->template get<Ps>().value.hasDataDrivenPropertyDifference(other.template get<Ps>().value))... });
+ return result;
+ }
+ };
+};
+
+template <class...>
+struct ConcatenateProperties;
+
+template <class... As, class... Bs>
+struct ConcatenateProperties<TypeList<As...>, TypeList<Bs...>> {
+ using Type = Properties<As..., Bs...>;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/rapidjson_conversion.hpp b/src/mbgl/style/rapidjson_conversion.hpp
index 101fe67ec0..48a764ccb4 100644
--- a/src/mbgl/style/rapidjson_conversion.hpp
+++ b/src/mbgl/style/rapidjson_conversion.hpp
@@ -62,6 +62,13 @@ inline optional<float> toNumber(const JSValue& value) {
return value.GetDouble();
}
+inline optional<double> toDouble(const JSValue& value) {
+ if (!value.IsNumber()) {
+ return {};
+ }
+ return value.GetDouble();
+}
+
inline optional<std::string> toString(const JSValue& value) {
if (!value.IsString()) {
return {};
diff --git a/src/mbgl/style/source.cpp b/src/mbgl/style/source.cpp
index cfb268006b..e7701b8bec 100644
--- a/src/mbgl/style/source.cpp
+++ b/src/mbgl/style/source.cpp
@@ -1,16 +1,25 @@
#include <mbgl/style/source.hpp>
#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/util/logging.hpp>
namespace mbgl {
namespace style {
-Source::Source(SourceType type_, std::unique_ptr<Impl> baseImpl_)
- : baseImpl(std::move(baseImpl_)), type(type_) {
+static SourceObserver nullObserver;
+
+Source::Source(Immutable<Impl> impl)
+ : baseImpl(std::move(impl)),
+ observer(&nullObserver) {
}
Source::~Source() = default;
-const std::string& Source::getID() const {
+SourceType Source::getType() const {
+ return baseImpl->type;
+}
+
+std::string Source::getID() const {
return baseImpl->id;
}
@@ -18,5 +27,14 @@ optional<std::string> Source::getAttribution() const {
return baseImpl->getAttribution();
}
+void Source::setObserver(SourceObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver;
+}
+
+void Source::dumpDebugLogs() const {
+ Log::Info(Event::General, "Source::id: %s", getID().c_str());
+ Log::Info(Event::General, "Source::loaded: %d", loaded);
+}
+
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp
index 1e9405abbb..0683f847cb 100644
--- a/src/mbgl/style/source_impl.cpp
+++ b/src/mbgl/style/source_impl.cpp
@@ -1,26 +1,11 @@
#include <mbgl/style/source_impl.hpp>
-#include <mbgl/style/source_observer.hpp>
-#include <mbgl/util/logging.hpp>
namespace mbgl {
namespace style {
-static SourceObserver nullObserver;
-
-Source::Impl::Impl(SourceType type_, std::string id_, Source& base_)
+Source::Impl::Impl(SourceType type_, std::string id_)
: type(type_),
- id(std::move(id_)),
- base(base_),
- observer(&nullObserver) {
-}
-
-void Source::Impl::dumpDebugLogs() const {
- Log::Info(Event::General, "Source::id: %s", base.getID().c_str());
- Log::Info(Event::General, "Source::loaded: %d", loaded);
-}
-
-void Source::Impl::setObserver(SourceObserver* observer_) {
- observer = observer_ ? observer_ : &nullObserver;
+ id(std::move(id_)) {
}
} // namespace style
diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp
index 2514ec5120..42da97345a 100644
--- a/src/mbgl/style/source_impl.hpp
+++ b/src/mbgl/style/source_impl.hpp
@@ -3,35 +3,30 @@
#include <mbgl/style/source.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <string>
+
namespace mbgl {
-class FileSource;
class RenderSource;
namespace style {
class SourceObserver;
-class Source::Impl : private util::noncopyable {
+class Source::Impl {
public:
- Impl(SourceType, std::string id, Source&);
virtual ~Impl() = default;
- virtual void loadDescription(FileSource&) = 0;
- virtual std::unique_ptr<RenderSource> createRenderSource() const = 0;
+ Impl& operator=(const Impl&) = delete;
- virtual optional<std::string> getAttribution() const { return {}; };
+ virtual optional<std::string> getAttribution() const = 0;
const SourceType type;
const std::string id;
- bool loaded = false;
- Source& base;
-
- void setObserver(SourceObserver*);
- SourceObserver* observer = nullptr;
-
- void dumpDebugLogs() const;
+protected:
+ Impl(SourceType, std::string);
+ Impl(const Impl&) = default;
};
} // namespace style
diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp
index 992f82b1e7..4e3478322d 100644
--- a/src/mbgl/style/sources/geojson_source.cpp
+++ b/src/mbgl/style/sources/geojson_source.cpp
@@ -1,27 +1,81 @@
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/sources/geojson_source_impl.hpp>
#include <mbgl/style/source_observer.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/logging.hpp>
namespace mbgl {
namespace style {
GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions& options)
- : Source(SourceType::GeoJSON,
- std::make_unique<GeoJSONSource::Impl>(std::move(id), *this, options)),
- impl(static_cast<Impl*>(baseImpl.get())) {
+ : Source(makeMutable<Impl>(std::move(id), options)) {
}
-void GeoJSONSource::setURL(const std::string& url) {
- impl->setURL(url);
+GeoJSONSource::~GeoJSONSource() = default;
+
+const GeoJSONSource::Impl& GeoJSONSource::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
+}
+
+void GeoJSONSource::setURL(const std::string& url_) {
+ url = std::move(url_);
+
+ // Signal that the source description needs a reload
+ if (loaded || req) {
+ loaded = false;
+ req.reset();
+ observer->onSourceDescriptionChanged(*this);
+ }
}
void GeoJSONSource::setGeoJSON(const mapbox::geojson::geojson& geoJSON) {
- impl->setGeoJSON(geoJSON);
- impl->observer->onSourceChanged(*this);
+ req.reset();
+ baseImpl = makeMutable<Impl>(impl(), geoJSON);
+ observer->onSourceChanged(*this);
}
optional<std::string> GeoJSONSource::getURL() const {
- return impl->getURL();
+ return url;
+}
+
+void GeoJSONSource::loadDescription(FileSource& fileSource) {
+ if (!url) {
+ loaded = true;
+ return;
+ }
+
+ if (req) {
+ return;
+ }
+
+ req = fileSource.request(Resource::source(*url), [this](Response res) {
+ if (res.error) {
+ observer->onSourceError(
+ *this, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ observer->onSourceError(
+ *this, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON")));
+ } else {
+ conversion::Error error;
+ optional<GeoJSON> geoJSON = conversion::convertJSON<GeoJSON>(*res.data, error);
+ if (!geoJSON) {
+ Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s",
+ error.message.c_str());
+ // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for
+ // tiles to load.
+ baseImpl = makeMutable<Impl>(impl(), GeoJSON{ FeatureCollection{} });
+ } else {
+ baseImpl = makeMutable<Impl>(impl(), *geoJSON);
+ }
+
+ loaded = true;
+ observer->onSourceLoaded(*this);
+ }
+ });
}
} // namespace style
diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp
index 1a686ff9bc..efa4018b46 100644
--- a/src/mbgl/style/sources/geojson_source_impl.cpp
+++ b/src/mbgl/style/sources/geojson_source_impl.cpp
@@ -1,16 +1,13 @@
#include <mbgl/style/sources/geojson_source_impl.hpp>
-#include <mbgl/style/conversion/json.hpp>
-#include <mbgl/style/conversion/geojson.hpp>
-#include <mbgl/style/source_observer.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/tile/tile_id.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/renderer/sources/render_geojson_source.hpp>
-#include <mbgl/util/constants.cpp>
-#include <mbgl/util/logging.hpp>
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
#include <supercluster.hpp>
+#include <cmath>
+
namespace mbgl {
namespace style {
@@ -42,33 +39,14 @@ private:
mapbox::supercluster::Supercluster impl;
};
-GeoJSONSource::Impl::Impl(std::string id_, Source& base_, const GeoJSONOptions options_)
- : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), options(options_) {
-}
-
-GeoJSONSource::Impl::~Impl() = default;
-
-void GeoJSONSource::Impl::setURL(std::string url_) {
- url = std::move(url_);
-
- // Signal that the source description needs a reload
- if (loaded || req) {
- loaded = false;
- req.reset();
- observer->onSourceDescriptionChanged(base);
- }
-}
-
-optional<std::string> GeoJSONSource::Impl::getURL() const {
- return url;
-}
-
-void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) {
- req.reset();
- _setGeoJSON(geoJSON);
+GeoJSONSource::Impl::Impl(std::string id_, GeoJSONOptions options_)
+ : Source::Impl(SourceType::GeoJSON, std::move(id_)),
+ options(std::move(options_)) {
}
-void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) {
+GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON)
+ : Source::Impl(other),
+ options(other.options) {
double scale = util::EXTENT / util::tileSize;
if (options.cluster
@@ -77,60 +55,20 @@ void GeoJSONSource::Impl::_setGeoJSON(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);
}
}
-void GeoJSONSource::Impl::loadDescription(FileSource& fileSource) {
- if (!url) {
- loaded = true;
- return;
- }
-
- if (req) {
- return;
- }
-
- req = fileSource.request(Resource::source(*url), [this](Response res) {
- if (res.error) {
- observer->onSourceError(
- base, std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- observer->onSourceError(
- base, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON")));
- } else {
- conversion::Error error;
- optional<GeoJSON> geoJSON = conversion::convertJSON<GeoJSON>(*res.data, error);
- if (!geoJSON) {
- Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s",
- error.message.c_str());
- // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for
- // tiles to load.
- _setGeoJSON(GeoJSON{ FeatureCollection{} });
- } else {
- _setGeoJSON(*geoJSON);
- }
-
- loaded = true;
- observer->onSourceLoaded(base);
- }
- });
-}
-
-std::unique_ptr<RenderSource> GeoJSONSource::Impl::createRenderSource() const {
- return std::make_unique<RenderGeoJSONSource>(*this);
-}
+GeoJSONSource::Impl::~Impl() = default;
Range<uint8_t> GeoJSONSource::Impl::getZoomRange() const {
return { 0, options.maxzoom };
@@ -140,5 +78,9 @@ GeoJSONData* GeoJSONSource::Impl::getData() const {
return data.get();
}
+optional<std::string> GeoJSONSource::Impl::getAttribution() const {
+ return {};
+}
+
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp
index dece1269f8..a524bab9f2 100644
--- a/src/mbgl/style/sources/geojson_source_impl.hpp
+++ b/src/mbgl/style/sources/geojson_source_impl.hpp
@@ -2,7 +2,7 @@
#include <mbgl/style/source_impl.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
-#include <mbgl/util/variant.hpp>
+#include <mbgl/util/range.hpp>
namespace mbgl {
@@ -19,25 +19,17 @@ public:
class GeoJSONSource::Impl : public Source::Impl {
public:
- Impl(std::string id, Source&, const GeoJSONOptions);
+ Impl(std::string id, GeoJSONOptions);
+ Impl(const GeoJSONSource::Impl&, const GeoJSON&);
~Impl() final;
- void setURL(std::string);
- optional<std::string> getURL() const;
Range<uint8_t> getZoomRange() const;
-
- void setGeoJSON(const GeoJSON&);
GeoJSONData* getData() const;
- void loadDescription(FileSource&) final;
- std::unique_ptr<RenderSource> createRenderSource() const final;
+ optional<std::string> getAttribution() const final;
private:
- void _setGeoJSON(const GeoJSON&);
-
GeoJSONOptions options;
- optional<std::string> url;
- std::unique_ptr<AsyncRequest> req;
std::unique_ptr<GeoJSONData> data;
};
diff --git a/src/mbgl/style/sources/image_source.cpp b/src/mbgl/style/sources/image_source.cpp
new file mode 100644
index 0000000000..9b60ba1a48
--- /dev/null
+++ b/src/mbgl/style/sources/image_source.cpp
@@ -0,0 +1,84 @@
+#include <mbgl/style/sources/image_source.hpp>
+#include <mbgl/style/sources/image_source_impl.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/util/premultiply.hpp>
+#include <mbgl/storage/file_source.hpp>
+
+namespace mbgl {
+namespace style {
+
+ImageSource::ImageSource(std::string id, const std::array<LatLng, 4> coords_)
+ : Source(makeMutable<Impl>(std::move(id), coords_)) {
+}
+
+ImageSource::~ImageSource() = default;
+
+const ImageSource::Impl& ImageSource::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
+}
+
+void ImageSource::setCoordinates(const std::array<LatLng, 4>& coords_) {
+ baseImpl = makeMutable<Impl>(impl(), coords_);
+ observer->onSourceChanged(*this);
+}
+
+std::array<LatLng, 4> ImageSource::getCoordinates() const {
+ return impl().getCoordinates();
+}
+
+void ImageSource::setURL(const std::string& url_) {
+ url = std::move(url_);
+ // Signal that the source description needs a reload
+ if (loaded || req) {
+ loaded = false;
+ req.reset();
+ observer->onSourceDescriptionChanged(*this);
+ }
+}
+
+void ImageSource::setImage(PremultipliedImage&& image_) {
+ url = {};
+ if (req) {
+ req.reset();
+ }
+ loaded = true;
+ baseImpl = makeMutable<Impl>(impl(), std::move(image_));
+ observer->onSourceChanged(*this);
+}
+
+optional<std::string> ImageSource::getURL() const {
+ return url;
+}
+
+void ImageSource::loadDescription(FileSource& fileSource) {
+ if (!url) {
+ loaded = true;
+ }
+
+ if (req || loaded) {
+ return;
+ }
+ const Resource imageResource { Resource::Image, *url, {}, Resource::Necessity::Required };
+
+ req = fileSource.request(imageResource, [this](Response res) {
+ if (res.error) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty image url")));
+ } else {
+ try {
+ baseImpl = makeMutable<Impl>(impl(), decodeImage(*res.data));
+ } catch (...) {
+ observer->onSourceError(*this, std::current_exception());
+ }
+ loaded = true;
+ observer->onSourceLoaded(*this);
+ }
+ });
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/image_source_impl.cpp b/src/mbgl/style/sources/image_source_impl.cpp
new file mode 100644
index 0000000000..c1f31dbdc6
--- /dev/null
+++ b/src/mbgl/style/sources/image_source_impl.cpp
@@ -0,0 +1,38 @@
+#include <mbgl/style/sources/image_source_impl.hpp>
+#include <mbgl/util/geo.hpp>
+
+namespace mbgl {
+namespace style {
+
+ImageSource::Impl::Impl(std::string id_, std::array<LatLng, 4> coords_)
+ : Source::Impl(SourceType::Image, std::move(id_)),
+ coords(std::move(coords_)) {
+}
+
+ImageSource::Impl::Impl(const Impl& other, std::array<LatLng, 4> coords_)
+ : Source::Impl(other),
+ coords(std::move(coords_)),
+ image(other.image) {
+}
+
+ImageSource::Impl::Impl(const Impl& rhs, PremultipliedImage&& image_)
+ : Source::Impl(rhs),
+ coords(rhs.coords),
+ image(std::make_shared<PremultipliedImage>(std::move(image_))) {
+}
+ImageSource::Impl::~Impl() = default;
+
+std::shared_ptr<PremultipliedImage> ImageSource::Impl::getImage() const {
+ return image;
+}
+
+std::array<LatLng, 4> ImageSource::Impl::getCoordinates() const {
+ return coords;
+}
+
+optional<std::string> ImageSource::Impl::getAttribution() const {
+ return {};
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/image_source_impl.hpp b/src/mbgl/style/sources/image_source_impl.hpp
new file mode 100644
index 0000000000..1e1b005a32
--- /dev/null
+++ b/src/mbgl/style/sources/image_source_impl.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/sources/image_source.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/geo.hpp>
+
+namespace mbgl {
+
+namespace style {
+
+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, PremultipliedImage&& image);
+
+ ~Impl() final;
+
+ std::shared_ptr<PremultipliedImage> getImage() const;
+ std::array<LatLng, 4> getCoordinates() const;
+
+ optional<std::string> getAttribution() const final;
+private:
+ std::array<LatLng, 4> coords;
+ std::shared_ptr<PremultipliedImage> image;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp
index 94fdbcef12..0a0412a4ed 100644
--- a/src/mbgl/style/sources/raster_source.cpp
+++ b/src/mbgl/style/sources/raster_source.cpp
@@ -1,21 +1,81 @@
#include <mbgl/style/sources/raster_source.hpp>
#include <mbgl/style/sources/raster_source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/tileset.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/mapbox.hpp>
namespace mbgl {
namespace style {
-RasterSource::RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize)
- : Source(SourceType::Raster, std::make_unique<RasterSource::Impl>(std::move(id), *this, std::move(urlOrTileset), tileSize)),
- impl(static_cast<Impl*>(baseImpl.get())) {
+RasterSource::RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset_, uint16_t tileSize)
+ : Source(makeMutable<Impl>(std::move(id), tileSize)),
+ urlOrTileset(std::move(urlOrTileset_)) {
+}
+
+RasterSource::~RasterSource() = default;
+
+const RasterSource::Impl& RasterSource::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
+}
+
+const variant<std::string, Tileset>& RasterSource::getURLOrTileset() const {
+ return urlOrTileset;
}
optional<std::string> RasterSource::getURL() const {
- auto urlOrTileset = impl->getURLOrTileset();
- if (urlOrTileset.is<std::string>()) {
- return urlOrTileset.get<std::string>();
- } else {
+ if (urlOrTileset.is<Tileset>()) {
return {};
}
+
+ return urlOrTileset.get<std::string>();
+}
+
+uint16_t RasterSource::getTileSize() const {
+ return impl().getTileSize();
+}
+
+void RasterSource::loadDescription(FileSource& fileSource) {
+ if (urlOrTileset.is<Tileset>()) {
+ baseImpl = makeMutable<Impl>(impl(), urlOrTileset.get<Tileset>());
+ loaded = true;
+ return;
+ }
+
+ if (req) {
+ return;
+ }
+
+ const std::string& url = urlOrTileset.get<std::string>();
+ req = fileSource.request(Resource::source(url), [this, url](Response res) {
+ if (res.error) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON")));
+ } else {
+ conversion::Error error;
+ optional<Tileset> tileset = conversion::convertJSON<Tileset>(*res.data, error);
+ if (!tileset) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.message)));
+ return;
+ }
+
+ util::mapbox::canonicalizeTileset(*tileset, url, getType(), getTileSize());
+ bool changed = impl().getTileset() != *tileset;
+
+ baseImpl = makeMutable<Impl>(impl(), *tileset);
+ loaded = true;
+
+ observer->onSourceLoaded(*this);
+
+ if (changed) {
+ observer->onSourceChanged(*this);
+ }
+ }
+ });
}
} // namespace style
diff --git a/src/mbgl/style/sources/raster_source_impl.cpp b/src/mbgl/style/sources/raster_source_impl.cpp
index b85d221f2e..50dae1f07e 100644
--- a/src/mbgl/style/sources/raster_source_impl.cpp
+++ b/src/mbgl/style/sources/raster_source_impl.cpp
@@ -1,17 +1,32 @@
#include <mbgl/style/sources/raster_source_impl.hpp>
-#include <mbgl/renderer/sources/render_raster_source.hpp>
namespace mbgl {
namespace style {
-RasterSource::Impl::Impl(std::string id_, Source& base_,
- variant<std::string, Tileset> urlOrTileset_,
- uint16_t tileSize_)
- : TileSourceImpl(SourceType::Raster, std::move(id_), base_, std::move(urlOrTileset_), tileSize_) {
+RasterSource::Impl::Impl(std::string id_, uint16_t tileSize_)
+ : Source::Impl(SourceType::Raster, std::move(id_)),
+ tileSize(tileSize_) {
}
-std::unique_ptr<RenderSource> RasterSource::Impl::createRenderSource() const {
- return std::make_unique<RenderRasterSource>(*this);
+RasterSource::Impl::Impl(const Impl& other, Tileset tileset_)
+ : Source::Impl(other),
+ tileSize(other.tileSize),
+ tileset(std::move(tileset_)) {
+}
+
+uint16_t RasterSource::Impl::getTileSize() const {
+ return tileSize;
+}
+
+optional<Tileset> RasterSource::Impl::getTileset() const {
+ return tileset;
+}
+
+optional<std::string> RasterSource::Impl::getAttribution() const {
+ if (!tileset) {
+ return {};
+ }
+ return tileset->attribution;
}
} // namespace style
diff --git a/src/mbgl/style/sources/raster_source_impl.hpp b/src/mbgl/style/sources/raster_source_impl.hpp
index 4bc76560f8..c41d5485b2 100644
--- a/src/mbgl/style/sources/raster_source_impl.hpp
+++ b/src/mbgl/style/sources/raster_source_impl.hpp
@@ -1,16 +1,24 @@
#pragma once
#include <mbgl/style/sources/raster_source.hpp>
-#include <mbgl/style/tile_source_impl.hpp>
+#include <mbgl/style/source_impl.hpp>
namespace mbgl {
namespace style {
-class RasterSource::Impl : public TileSourceImpl {
+class RasterSource::Impl : public Source::Impl {
public:
- Impl(std::string id, Source&, variant<std::string, Tileset>, uint16_t tileSize);
+ Impl(std::string id, uint16_t tileSize);
+ Impl(const Impl&, Tileset);
- std::unique_ptr<RenderSource> createRenderSource() const final;
+ optional<Tileset> getTileset() const;
+ uint16_t getTileSize() const;
+
+ optional<std::string> getAttribution() const final;
+
+private:
+ uint16_t tileSize;
+ optional<Tileset> tileset;
};
} // namespace style
diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp
index 4bcd3b8985..ccdd453c75 100644
--- a/src/mbgl/style/sources/vector_source.cpp
+++ b/src/mbgl/style/sources/vector_source.cpp
@@ -1,21 +1,78 @@
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/sources/vector_source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/tileset.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/mapbox.hpp>
+#include <mbgl/util/constants.hpp>
namespace mbgl {
namespace style {
-VectorSource::VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset)
- : Source(SourceType::Vector, std::make_unique<VectorSource::Impl>(std::move(id), *this, std::move(urlOrTileset))),
- impl(static_cast<Impl*>(baseImpl.get())) {
+VectorSource::VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset_)
+ : Source(makeMutable<Impl>(std::move(id))),
+ urlOrTileset(std::move(urlOrTileset_)) {
+}
+
+VectorSource::~VectorSource() = default;
+
+const VectorSource::Impl& VectorSource::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
+}
+
+const variant<std::string, Tileset>& VectorSource::getURLOrTileset() const {
+ return urlOrTileset;
}
optional<std::string> VectorSource::getURL() const {
- auto urlOrTileset = impl->getURLOrTileset();
- if (urlOrTileset.is<std::string>()) {
- return urlOrTileset.get<std::string>();
- } else {
+ if (urlOrTileset.is<Tileset>()) {
return {};
}
+
+ return urlOrTileset.get<std::string>();
+}
+
+void VectorSource::loadDescription(FileSource& fileSource) {
+ if (urlOrTileset.is<Tileset>()) {
+ baseImpl = makeMutable<Impl>(impl(), urlOrTileset.get<Tileset>());
+ loaded = true;
+ return;
+ }
+
+ if (req) {
+ return;
+ }
+
+ const std::string& url = urlOrTileset.get<std::string>();
+ req = fileSource.request(Resource::source(url), [this, url](Response res) {
+ if (res.error) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON")));
+ } else {
+ conversion::Error error;
+ optional<Tileset> tileset = conversion::convertJSON<Tileset>(*res.data, error);
+ if (!tileset) {
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(error.message)));
+ return;
+ }
+
+ util::mapbox::canonicalizeTileset(*tileset, url, getType(), util::tileSize);
+ bool changed = impl().getTileset() != *tileset;
+
+ baseImpl = makeMutable<Impl>(impl(), *tileset);
+ loaded = true;
+
+ observer->onSourceLoaded(*this);
+
+ if (changed) {
+ observer->onSourceChanged(*this);
+ }
+ }
+ });
}
} // namespace style
diff --git a/src/mbgl/style/sources/vector_source_impl.cpp b/src/mbgl/style/sources/vector_source_impl.cpp
index 158abf8575..b06f0557bf 100644
--- a/src/mbgl/style/sources/vector_source_impl.cpp
+++ b/src/mbgl/style/sources/vector_source_impl.cpp
@@ -1,16 +1,26 @@
#include <mbgl/style/sources/vector_source_impl.hpp>
-#include <mbgl/renderer/sources/render_vector_source.hpp>
-#include <mbgl/util/constants.hpp>
namespace mbgl {
namespace style {
-VectorSource::Impl::Impl(std::string id_, Source& base_, variant<std::string, Tileset> urlOrTileset_)
- : TileSourceImpl(SourceType::Vector, std::move(id_), base_, std::move(urlOrTileset_), util::tileSize) {
+VectorSource::Impl::Impl(std::string id_)
+ : Source::Impl(SourceType::Vector, std::move(id_)) {
}
-std::unique_ptr<RenderSource> VectorSource::Impl::createRenderSource() const {
- return std::make_unique<RenderVectorSource>(*this);
+VectorSource::Impl::Impl(const Impl& other, Tileset tileset_)
+ : Source::Impl(other),
+ tileset(std::move(tileset_)) {
+}
+
+optional<Tileset> VectorSource::Impl::getTileset() const {
+ return tileset;
+}
+
+optional<std::string> VectorSource::Impl::getAttribution() const {
+ if (!tileset) {
+ return {};
+ }
+ return tileset->attribution;
}
} // namespace style
diff --git a/src/mbgl/style/sources/vector_source_impl.hpp b/src/mbgl/style/sources/vector_source_impl.hpp
index 844739948c..5e559b9266 100644
--- a/src/mbgl/style/sources/vector_source_impl.hpp
+++ b/src/mbgl/style/sources/vector_source_impl.hpp
@@ -1,16 +1,22 @@
#pragma once
#include <mbgl/style/sources/vector_source.hpp>
-#include <mbgl/style/tile_source_impl.hpp>
+#include <mbgl/style/source_impl.hpp>
namespace mbgl {
namespace style {
-class VectorSource::Impl : public TileSourceImpl {
+class VectorSource::Impl : public Source::Impl {
public:
- Impl(std::string id, Source&, variant<std::string, Tileset>);
+ Impl(std::string id);
+ Impl(const Impl&, Tileset);
- std::unique_ptr<RenderSource> createRenderSource() const final;
+ optional<Tileset> getTileset() const;
+
+ optional<std::string> getAttribution() const final;
+
+private:
+ optional<Tileset> tileset;
};
} // namespace style
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index f1ac22082b..bd8631fc52 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -1,813 +1,133 @@
#include <mbgl/style/style.hpp>
-#include <mbgl/style/observer.hpp>
-#include <mbgl/style/source_impl.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
-#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/style/layers/custom_layer.hpp>
-#include <mbgl/style/layers/custom_layer_impl.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
-#include <mbgl/style/layers/background_layer_impl.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/circle_layer.hpp>
-#include <mbgl/style/layers/raster_layer.hpp>
-#include <mbgl/style/layer_impl.hpp>
-#include <mbgl/style/parser.hpp>
-#include <mbgl/style/transition_options.hpp>
-#include <mbgl/style/class_dictionary.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/cascade_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/render_background_layer.hpp>
-#include <mbgl/renderer/render_circle_layer.hpp>
-#include <mbgl/renderer/render_custom_layer.hpp>
-#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
-#include <mbgl/renderer/render_fill_layer.hpp>
-#include <mbgl/renderer/render_line_layer.hpp>
-#include <mbgl/renderer/render_raster_layer.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/exception.hpp>
-#include <mbgl/util/geometry.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/logging.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/math/minmax.hpp>
-#include <mbgl/map/query.hpp>
-
-#include <algorithm>
+#include <mbgl/style/style_impl.hpp>
+#include <mbgl/style/light.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/source.hpp>
+#include <mbgl/style/layer.hpp>
namespace mbgl {
namespace style {
-static Observer nullObserver;
-
-struct QueueSourceReloadVisitor {
- UpdateBatch& updateBatch;
-
- // No need to reload sources for these types; their visibility can change but
- // they don't participate in layout.
- void operator()(CustomLayer&) {}
- void operator()(RasterLayer&) {}
- void operator()(BackgroundLayer&) {}
-
- template <class VectorLayer>
- void operator()(VectorLayer& layer) {
- updateBatch.sourceIDs.insert(layer.getSourceID());
- }
-};
-
-Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio)
- : scheduler(scheduler_),
- fileSource(fileSource_),
- glyphAtlas(std::make_unique<GlyphAtlas>(Size{ 2048, 2048 }, fileSource)),
- spriteAtlas(std::make_unique<SpriteAtlas>(Size{ 1024, 1024 }, pixelRatio)),
- lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
- light(std::make_unique<Light>()),
- renderLight(std::make_unique<RenderLight>(light->impl)),
- observer(&nullObserver) {
- glyphAtlas->setObserver(this);
- spriteAtlas->setObserver(this);
- light->impl->setObserver(this);
+Style::Style(Scheduler& scheduler, FileSource& fileSource, float pixelRatio)
+ : impl(std::make_unique<Impl>(scheduler, fileSource, pixelRatio)) {
}
-Style::~Style() {
- for (const auto& source : sources) {
- source->baseImpl->setObserver(nullptr);
- }
-
- for (const auto& layer : layers) {
- if (CustomLayer* customLayer = layer->as<CustomLayer>()) {
- customLayer->impl->deinitialize();
- }
- }
+Style::~Style() = default;
- glyphAtlas->setObserver(nullptr);
- spriteAtlas->setObserver(nullptr);
- light->impl->setObserver(nullptr);
+void Style::loadJSON(const std::string& json) {
+ impl->loadJSON(json);
}
-bool Style::addClass(const std::string& className) {
- if (hasClass(className)) return false;
- classes.push_back(className);
- return true;
+void Style::loadURL(const std::string& url) {
+ impl->loadURL(url);
}
-bool Style::hasClass(const std::string& className) const {
- return std::find(classes.begin(), classes.end(), className) != classes.end();
+std::string Style::getJSON() const {
+ return impl->getJSON();
}
-bool Style::removeClass(const std::string& className) {
- const auto it = std::find(classes.begin(), classes.end(), className);
- if (it != classes.end()) {
- classes.erase(it);
- return true;
- }
- return false;
+std::string Style::getURL() const {
+ return impl->getURL();
}
-void Style::setClasses(const std::vector<std::string>& classNames) {
- classes = classNames;
+std::string Style::getName() const {
+ return impl->getName();
}
-std::vector<std::string> Style::getClasses() const {
- return classes;
-}
-
-void Style::setTransitionOptions(const TransitionOptions& options) {
- transitionOptions = options;
+CameraOptions Style::getDefaultCamera() const {
+ return impl->getDefaultCamera();
}
TransitionOptions Style::getTransitionOptions() const {
- return transitionOptions;
-}
-
-void Style::setJSON(const std::string& json) {
- sources.clear();
- renderSources.clear();
- layers.clear();
- renderLayers.clear();
- classes.clear();
- transitionOptions = {};
- updateBatch = {};
-
- Parser parser;
- auto error = parser.parse(json);
-
- if (error) {
- std::string message = "Failed to parse style: " + util::toString(error);
- Log::Error(Event::ParseStyle, message.c_str());
- observer->onStyleError(std::make_exception_ptr(util::StyleParseException(message)));
- observer->onResourceError(error);
- return;
- }
-
- for (auto& source : parser.sources) {
- addSource(std::move(source));
- }
-
- for (auto& layer : parser.layers) {
- addLayer(std::move(layer));
- }
-
- name = parser.name;
- defaultLatLng = parser.latLng;
- defaultZoom = parser.zoom;
- defaultBearing = parser.bearing;
- defaultPitch = parser.pitch;
- setLight(std::make_unique<Light>(parser.light));
-
- glyphAtlas->setURL(parser.glyphURL);
- spriteAtlas->load(parser.spriteURL, scheduler, fileSource);
-
- loaded = true;
-
- observer->onStyleLoaded();
-}
-
-void Style::addSource(std::unique_ptr<Source> source) {
- // Guard against duplicate source ids
- auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& existing) {
- return existing->getID() == source->getID();
- });
-
- if (it != sources.end()) {
- std::string msg = "Source " + source->getID() + " already exists";
- throw std::runtime_error(msg.c_str());
- }
-
- source->baseImpl->setObserver(this);
- source->baseImpl->loadDescription(fileSource);
-
- std::unique_ptr<RenderSource> renderSource = source->baseImpl->createRenderSource();
- renderSource->setObserver(this);
- renderSources.emplace_back(std::move(renderSource));
-
- sources.emplace_back(std::move(source));
-}
-
-struct SourceIdUsageEvaluator {
- const std::string& sourceId;
-
- bool operator()(BackgroundLayer&) { return false; }
- bool operator()(CustomLayer&) { return false; }
-
- template <class LayerType>
- bool operator()(LayerType& layer) {
- return layer.getSourceID() == sourceId;
- }
-};
-
-std::unique_ptr<Source> Style::removeSource(const std::string& id) {
- // Check if source is in use
- SourceIdUsageEvaluator sourceIdEvaluator {id};
- auto layerIt = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
- return layer->accept(sourceIdEvaluator);
- });
-
- if (layerIt != layers.end()) {
- Log::Warning(Event::General, "Source '%s' is in use, cannot remove", id.c_str());
- return nullptr;
- }
-
- auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) {
- return source->getID() == id;
- });
-
- if (it == sources.end()) {
- return nullptr;
- }
-
- util::erase_if(renderSources, [&](const auto& source) {
- return source->baseImpl.id == id;
- });
-
- auto source = std::move(*it);
- source->baseImpl->setObserver(nullptr);
- sources.erase(it);
- updateBatch.sourceIDs.erase(id);
-
- return source;
-}
-
-std::vector<const Layer*> Style::getLayers() const {
- std::vector<const Layer*> result;
- result.reserve(layers.size());
- for (const auto& layer : layers) {
- result.push_back(layer.get());
- }
- return result;
-}
-
-std::vector<Layer*> Style::getLayers() {
- std::vector<Layer*> result;
- result.reserve(layers.size());
- for (auto& layer : layers) {
- result.push_back(layer.get());
- }
- return result;
-}
-
-std::vector<std::unique_ptr<Layer>>::const_iterator Style::findLayer(const std::string& id) const {
- return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
- return layer->baseImpl->id == id;
- });
-}
-
-Layer* Style::getLayer(const std::string& id) const {
- auto it = findLayer(id);
- return it != layers.end() ? it->get() : nullptr;
-}
-
-Layer* Style::addLayer(std::unique_ptr<Layer> layer, optional<std::string> before) {
- // TODO: verify source
-
- // Guard against duplicate layer ids
- auto it = std::find_if(layers.begin(), layers.end(), [&](const auto& existing) {
- return existing->getID() == layer->getID();
- });
-
- if (it != layers.end()) {
- throw std::runtime_error(std::string{"Layer "} + layer->getID() + " already exists");
- }
-
- if (CustomLayer* customLayer = layer->as<CustomLayer>()) {
- customLayer->impl->initialize();
- }
-
- layer->baseImpl->setObserver(this);
- layer->accept(QueueSourceReloadVisitor { updateBatch });
-
- auto added = layers.emplace(before ? findLayer(*before) : layers.end(), std::move(layer))->get();
- renderLayers.emplace(before ? findRenderLayer(*before) : renderLayers.end(), added->baseImpl->createRenderLayer());
- return std::move(added);
-}
-
-std::unique_ptr<Layer> Style::removeLayer(const std::string& id) {
- auto it = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
- return layer->baseImpl->id == id;
- });
-
- if (it == layers.end())
- return nullptr;
-
- auto layer = std::move(*it);
-
- if (CustomLayer* customLayer = layer->as<CustomLayer>()) {
- customLayer->impl->deinitialize();
- }
-
- layer->baseImpl->setObserver(nullptr);
- layers.erase(it);
- removeRenderLayer(id);
- return layer;
-}
-
-std::vector<const RenderLayer*> Style::getRenderLayers() const {
- std::vector<const RenderLayer*> result;
- result.reserve(renderLayers.size());
- for (const auto& layer : renderLayers) {
- result.push_back(layer.get());
- }
- return result;
-}
-
-std::vector<RenderLayer*> Style::getRenderLayers() {
- std::vector<RenderLayer*> result;
- result.reserve(renderLayers.size());
- for (auto& layer : renderLayers) {
- result.push_back(layer.get());
- }
- return result;
-}
-
-std::vector<std::unique_ptr<RenderLayer>>::const_iterator Style::findRenderLayer(const std::string& id) const {
- return std::find_if(renderLayers.begin(), renderLayers.end(), [&](const auto& layer) {
- return layer->baseImpl.id == id;
- });
-}
-
-RenderLayer* Style::getRenderLayer(const std::string& id) const {
- auto it = findRenderLayer(id);
- return it != renderLayers.end() ? it->get() : nullptr;
-}
-
-void Style::removeRenderLayer(const std::string& id) {
- auto it = std::find_if(renderLayers.begin(), renderLayers.end(), [&](const auto& layer) {
- return layer->baseImpl.id == id;
- });
-
- if (it != renderLayers.end()) {
- renderLayers.erase(it);
- }
-}
-
-void Style::setLight(std::unique_ptr<Light> light_) {
- light = std::move(light_);
- light->impl->setObserver(this);
-
- // Copy renderlight to preserve the initialised
- // transitioning light properties
- renderLight = renderLight->copy(light->impl);
-
- onLightChanged(*light);
-}
-
-Light* Style::getLight() const {
- return light.get();
-}
-
-RenderLight* Style::getRenderLight() const {
- return renderLight.get();
+ return impl->getTransitionOptions();
}
-std::string Style::getName() const {
- return name;
+void Style::setTransitionOptions(const TransitionOptions& options) {
+ impl->mutated = true;
+ impl->setTransitionOptions(options);
}
-LatLng Style::getDefaultLatLng() const {
- return defaultLatLng;
+void Style::setLight(std::unique_ptr<Light> light) {
+ impl->setLight(std::move(light));
}
-double Style::getDefaultZoom() const {
- return defaultZoom;
+Light* Style::getLight() {
+ impl->mutated = true;
+ return impl->getLight();
}
-double Style::getDefaultBearing() const {
- return defaultBearing;
+const Light* Style::getLight() const {
+ return impl->getLight();
}
-double Style::getDefaultPitch() const {
- return defaultPitch;
+const Image* Style::getImage(const std::string& name) const {
+ return impl->getImage(name);
}
-void Style::update(const UpdateParameters& parameters) {
- bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint);
-
- std::vector<ClassID> classIDs;
- for (const auto& className : classes) {
- classIDs.push_back(ClassDictionary::Get().lookup(className));
- }
- classIDs.push_back(ClassID::Default);
-
- const CascadeParameters cascadeParameters {
- classIDs,
- parameters.timePoint,
- parameters.mode == MapMode::Continuous ? transitionOptions : TransitionOptions()
- };
-
- const PropertyEvaluationParameters evaluationParameters {
- zoomHistory,
- parameters.timePoint,
- parameters.mode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Duration::zero()
- };
-
- const TileParameters tileParameters(parameters.pixelRatio,
- parameters.debugOptions,
- parameters.transformState,
- parameters.scheduler,
- parameters.fileSource,
- parameters.mode,
- parameters.annotationManager,
- *this);
-
- const bool cascade = parameters.updateFlags & Update::Classes;
- const bool evaluate = cascade || zoomChanged || parameters.updateFlags & Update::RecalculateStyle;
-
- if (cascade) {
- renderLight->transition(cascadeParameters);
- }
-
- if (evaluate || renderLight->hasTransition()) {
- renderLight->evaluate(evaluationParameters);
- }
-
- for (const auto& renderSource : renderSources) {
- renderSource->enabled = false;
- }
-
- for (const auto& layer : renderLayers) {
- if (cascade) {
- layer->cascade(cascadeParameters);
- }
-
- if (evaluate || layer->hasTransition()) {
- layer->evaluate(evaluationParameters);
- }
-
- if (layer->needsRendering(zoomHistory.lastZoom)) {
- if (RenderSource* renderSource = getRenderSource(layer->baseImpl.source)) {
- renderSource->enabled = true;
- }
- }
- }
-
- for (const auto& renderSource : renderSources) {
- bool updated = updateBatch.sourceIDs.count(renderSource->baseImpl.id);
- if (renderSource->enabled) {
- if (updated) {
- renderSource->reloadTiles();
- }
- renderSource->updateTiles(tileParameters);
- } else if (updated) {
- renderSource->invalidateTiles();
- } else {
- renderSource->removeTiles();
- }
- }
-
- updateBatch.sourceIDs.clear();
+void Style::addImage(std::unique_ptr<Image> image) {
+ impl->mutated = true;
+ impl->addImage(std::move(image));
}
-std::vector<const Source*> Style::getSources() const {
- std::vector<const Source*> result;
- result.reserve(sources.size());
- for (const auto& source : sources) {
- result.push_back(source.get());
- }
- return result;
+void Style::removeImage(const std::string& name) {
+ impl->mutated = true;
+ impl->removeImage(name);
}
std::vector<Source*> Style::getSources() {
- std::vector<Source*> result;
- result.reserve(sources.size());
- for (auto& source : sources) {
- result.push_back(source.get());
- }
- return result;
+ impl->mutated = true;
+ return impl->getSources();
}
-Source* Style::getSource(const std::string& id) const {
- const auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) {
- return source->getID() == id;
- });
-
- return it != sources.end() ? it->get() : nullptr;
-}
-
-RenderSource* Style::getRenderSource(const std::string& id) const {
- const auto it = std::find_if(renderSources.begin(), renderSources.end(), [&](const auto& source) {
- return source->baseImpl.id == id;
- });
-
- return it != renderSources.end() ? it->get() : nullptr;
-}
-
-bool Style::hasTransitions() const {
- if (renderLight->hasTransition()) {
- return true;
- }
-
- for (const auto& layer : renderLayers) {
- if (layer->hasTransition()) {
- return true;
- }
- }
-
- return false;
-}
-
-bool Style::isLoaded() const {
- if (!loaded) {
- return false;
- }
-
- for (const auto& source: sources) {
- if (!source->baseImpl->loaded) {
- return false;
- }
- }
-
- for (const auto& renderSource: renderSources) {
- if (!renderSource->isLoaded()) {
- return false;
- }
- }
-
- if (!spriteAtlas->isLoaded()) {
- return false;
- }
-
- return true;
-}
-
-RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const {
- RenderData result;
-
- for (const auto& renderSource: renderSources) {
- if (renderSource->enabled) {
- result.sources.insert(renderSource.get());
- }
- }
-
- for (const auto& layer : renderLayers) {
- 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);
- continue;
- }
- const BackgroundPaintProperties::Evaluated& paint = background->evaluated;
- if (layer.get() == renderLayers[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);
- }
- continue;
- }
-
- if (layer->is<RenderCustomLayer>()) {
- result.order.emplace_back(*layer);
- continue;
- }
-
- RenderSource* source = getRenderSource(layer->baseImpl.source);
- if (!source) {
- Log::Warning(Event::Render, "can't find source for layer '%s'", layer->baseImpl.id.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 tileIt = sortedTiles.begin(); tileIt != sortedTiles.end(); ++tileIt) {
- auto& tile = tileIt->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);
- if (bucket) {
- sortedTilesForInsertion.emplace_back(tile);
- tile.used = true;
- }
- }
-
- result.order.emplace_back(*layer, std::move(sortedTilesForInsertion));
- }
-
- return result;
-}
-
-std::vector<Feature> Style::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 (Layer* layer = getLayer(layerID)) {
- sourceIDs.emplace(layer->baseImpl->source);
- }
- }
- for (const auto& sourceID : sourceIDs) {
- if (RenderSource* renderSource = getRenderSource(sourceID)) {
- auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, options);
- std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
- }
- }
- } else {
- for (const auto& renderSource : renderSources) {
- auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, 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& layer : renderLayers) {
- 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 Style::setSourceTileCacheSize(size_t size) {
- for (const auto& renderSource : renderSources) {
- renderSource->setCacheSize(size);
- }
-}
-
-void Style::onLowMemory() {
- for (const auto& renderSource : renderSources) {
- renderSource->onLowMemory();
- }
-}
-
-void Style::setObserver(style::Observer* observer_) {
- observer = observer_;
-}
-
-void Style::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) {
- observer->onGlyphsLoaded(fontStack, glyphRange);
-}
-
-void Style::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
- lastError = 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->onGlyphsError(fontStack, glyphRange, error);
- observer->onResourceError(error);
-}
-
-void Style::onSourceLoaded(Source& source) {
- observer->onSourceLoaded(source);
- observer->onUpdate(Update::Repaint);
-}
-
-void Style::onSourceChanged(Source& source) {
- observer->onSourceChanged(source);
- observer->onUpdate(Update::Repaint);
-}
-
-void Style::onSourceError(Source& source, std::exception_ptr error) {
- lastError = error;
- Log::Error(Event::Style, "Failed to load source %s: %s",
- source.getID().c_str(), util::toString(error).c_str());
- observer->onSourceError(source, error);
- observer->onResourceError(error);
-}
-
-void Style::onSourceDescriptionChanged(Source& source) {
- observer->onSourceDescriptionChanged(source);
- if (!source.baseImpl->loaded) {
- source.baseImpl->loadDescription(fileSource);
- }
-}
-
-void Style::onTileChanged(RenderSource&, const OverscaledTileID&) {
- observer->onUpdate(Update::Repaint);
+std::vector<const Source*> Style::getSources() const {
+ return const_cast<const Impl&>(*impl).getSources();
}
-void Style::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- lastError = 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);
+Source* Style::getSource(const std::string& id) {
+ impl->mutated = true;
+ return impl->getSource(id);
}
-void Style::onSpriteLoaded() {
- observer->onSpriteLoaded();
- observer->onUpdate(Update::Repaint); // For *-pattern properties.
+const Source* Style::getSource(const std::string& id) const {
+ return impl->getSource(id);
}
-void Style::onSpriteError(std::exception_ptr error) {
- lastError = error;
- Log::Error(Event::Style, "Failed to load sprite: %s", util::toString(error).c_str());
- observer->onSpriteError(error);
- observer->onResourceError(error);
+void Style::addSource(std::unique_ptr<Source> source) {
+ impl->mutated = true;
+ impl->addSource(std::move(source));
}
-void Style::onLayerFilterChanged(Layer& layer) {
- layer.accept(QueueSourceReloadVisitor { updateBatch });
- observer->onUpdate(Update::Repaint);
+std::unique_ptr<Source> Style::removeSource(const std::string& sourceID) {
+ impl->mutated = true;
+ return impl->removeSource(sourceID);
}
-void Style::onLayerVisibilityChanged(Layer& layer) {
- layer.accept(QueueSourceReloadVisitor { updateBatch });
- observer->onUpdate(Update::RecalculateStyle);
+std::vector<Layer*> Style::getLayers() {
+ impl->mutated = true;
+ return impl->getLayers();
}
-void Style::onLayerPaintPropertyChanged(Layer&) {
- observer->onUpdate(Update::RecalculateStyle | Update::Classes);
+std::vector<const Layer*> Style::getLayers() const {
+ return const_cast<const Impl&>(*impl).getLayers();
}
-void Style::onLayerDataDrivenPaintPropertyChanged(Layer& layer) {
- layer.accept(QueueSourceReloadVisitor { updateBatch });
- observer->onUpdate(Update::RecalculateStyle | Update::Classes);
+Layer* Style::getLayer(const std::string& layerID) {
+ impl->mutated = true;
+ return impl->getLayer(layerID);
}
-void Style::onLayerLayoutPropertyChanged(Layer& layer, const char * property) {
- layer.accept(QueueSourceReloadVisitor { updateBatch });
-
- // Recalculate the style for certain properties
- observer->onUpdate((strcmp(property, "icon-size") == 0 || strcmp(property, "text-size") == 0)
- ? Update::RecalculateStyle
- : Update::Repaint);
+const Layer* Style::getLayer(const std::string& layerID) const {
+ return impl->getLayer(layerID);
}
-void Style::onLightChanged(const Light&) {
- observer->onUpdate(Update::Classes | Update::RecalculateStyle);
+void Style::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& before) {
+ impl->mutated = true;
+ impl->addLayer(std::move(layer), before);
}
-void Style::dumpDebugLogs() const {
- for (const auto& source : sources) {
- source->baseImpl->dumpDebugLogs();
- }
-
- for (const auto& renderSource : renderSources) {
- renderSource->dumpDebugLogs();
- }
-
- spriteAtlas->dumpDebugLogs();
+std::unique_ptr<Layer> Style::removeLayer(const std::string& id) {
+ impl->mutated = true;
+ return impl->removeLayer(id);
}
} // namespace style
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
deleted file mode 100644
index 7d235dc665..0000000000
--- a/src/mbgl/style/style.hpp
+++ /dev/null
@@ -1,192 +0,0 @@
-#pragma once
-
-#include <mbgl/style/transition_options.hpp>
-#include <mbgl/style/observer.hpp>
-#include <mbgl/style/source_observer.hpp>
-#include <mbgl/renderer/render_source_observer.hpp>
-#include <mbgl/style/layer_observer.hpp>
-#include <mbgl/style/light_observer.hpp>
-#include <mbgl/style/update_batch.hpp>
-#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/renderer/render_light.hpp>
-#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/sprite/sprite_atlas_observer.hpp>
-#include <mbgl/map/mode.hpp>
-#include <mbgl/map/zoom_history.hpp>
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/feature.hpp>
-#include <mbgl/util/geo.hpp>
-
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace mbgl {
-
-class FileSource;
-class GlyphAtlas;
-class SpriteAtlas;
-class LineAtlas;
-class RenderData;
-class TransformState;
-class RenderedQueryOptions;
-class Scheduler;
-class RenderLayer;
-class RenderSource;
-class UpdateParameters;
-
-namespace style {
-
-class Layer;
-class QueryParameters;
-
-class Style : public GlyphAtlasObserver,
- public SpriteAtlasObserver,
- public SourceObserver,
- public RenderSourceObserver,
- public LayerObserver,
- public LightObserver,
- public util::noncopyable {
-public:
- Style(Scheduler&, FileSource&, float pixelRatio);
- ~Style() override;
-
- void setJSON(const std::string&);
-
- void setObserver(Observer*);
-
- bool isLoaded() const;
-
- void update(const UpdateParameters&);
-
- bool hasTransitions() const;
-
- std::exception_ptr getLastError() const {
- return lastError;
- }
-
- std::vector<const Source*> getSources() const;
- std::vector<Source*> getSources();
- Source* getSource(const std::string& id) const;
- void addSource(std::unique_ptr<Source>);
- std::unique_ptr<Source> removeSource(const std::string& sourceID);
-
- std::vector<const Layer*> getLayers() const;
- std::vector<Layer*> getLayers();
- Layer* getLayer(const std::string& id) const;
- Layer* addLayer(std::unique_ptr<Layer>,
- optional<std::string> beforeLayerID = {});
- std::unique_ptr<Layer> removeLayer(const std::string& layerID);
-
- // Should be moved to Impl eventually
- std::vector<const RenderLayer*> getRenderLayers() const;
- std::vector<RenderLayer*> getRenderLayers();
- RenderLayer* getRenderLayer(const std::string& id) const;
-
- std::string getName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
-
- bool addClass(const std::string&);
- bool removeClass(const std::string&);
- void setClasses(const std::vector<std::string>&);
-
- TransitionOptions getTransitionOptions() const;
- void setTransitionOptions(const TransitionOptions&);
-
- bool hasClass(const std::string&) const;
- std::vector<std::string> getClasses() const;
-
- void setLight(std::unique_ptr<Light>);
- Light* getLight() const;
- RenderLight* getRenderLight() const;
-
- RenderData getRenderData(MapDebugOptions, float angle) const;
-
- 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<GlyphAtlas> glyphAtlas;
- std::unique_ptr<SpriteAtlas> spriteAtlas;
- std::unique_ptr<LineAtlas> lineAtlas;
-
- RenderSource* getRenderSource(const std::string& id) const;
-
-private:
- std::vector<std::unique_ptr<Source>> sources;
- std::vector<std::unique_ptr<RenderSource>> renderSources;
-
- std::vector<std::unique_ptr<Layer>> layers;
- std::vector<std::unique_ptr<RenderLayer>> renderLayers;
- std::vector<std::string> classes;
- TransitionOptions transitionOptions;
-
- std::unique_ptr<Light> light;
- std::unique_ptr<RenderLight> renderLight;
-
- // Defaults
- std::string name;
- LatLng defaultLatLng;
- double defaultZoom = 0;
- double defaultBearing = 0;
- double defaultPitch = 0;
-
- std::vector<std::unique_ptr<Layer>>::const_iterator findLayer(const std::string& layerID) const;
- std::vector<std::unique_ptr<RenderLayer>>::const_iterator findRenderLayer(const std::string&) const;
-
- // GlyphStoreObserver implementation.
- void onGlyphsLoaded(const FontStack&, const GlyphRange&) override;
- void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
-
- // SpriteStoreObserver implementation.
- void onSpriteLoaded() override;
- void onSpriteError(std::exception_ptr) override;
-
- // SourceObserver implementation.
- void onSourceLoaded(Source&) override;
- void onSourceChanged(Source&) override;
- void onSourceError(Source&, std::exception_ptr) override;
- void onSourceDescriptionChanged(Source&) override;
- void onTileChanged(RenderSource&, const OverscaledTileID&) override;
- void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
-
- // LayerObserver implementation.
- void onLayerFilterChanged(Layer&) override;
- void onLayerVisibilityChanged(Layer&) override;
- void onLayerPaintPropertyChanged(Layer&) override;
- void onLayerDataDrivenPaintPropertyChanged(Layer&) override;
- void onLayerLayoutPropertyChanged(Layer&, const char *) override;
-
- // LightObserver implementation.
- void onLightChanged(const Light&) override;
-
- Observer nullObserver;
- Observer* observer = &nullObserver;
-
- std::exception_ptr lastError;
-
- UpdateBatch updateBatch;
- ZoomHistory zoomHistory;
-
- void removeRenderLayer(const std::string& layerID);
-
-public:
- bool loaded = false;
-};
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp
new file mode 100644
index 0000000000..37907d3f60
--- /dev/null
+++ b/src/mbgl/style/style_impl.cpp
@@ -0,0 +1,364 @@
+#include <mbgl/style/style_impl.hpp>
+#include <mbgl/style/observer.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/style/parser.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/sprite/sprite_loader.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+
+namespace mbgl {
+namespace style {
+
+static Observer nullObserver;
+
+Style::Impl::Impl(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio)
+ : scheduler(scheduler_),
+ fileSource(fileSource_),
+ spriteLoader(std::make_unique<SpriteLoader>(pixelRatio)),
+ light(std::make_unique<Light>()),
+ observer(&nullObserver) {
+ spriteLoader->setObserver(this);
+ light->setObserver(this);
+}
+
+Style::Impl::~Impl() = default;
+
+void Style::Impl::loadJSON(const std::string& json_) {
+ lastError = nullptr;
+ observer->onStyleLoading();
+
+ url.clear();
+ parse(json_);
+}
+
+void Style::Impl::loadURL(const std::string& url_) {
+ lastError = nullptr;
+ observer->onStyleLoading();
+
+ loaded = false;
+ url = url_;
+
+ styleRequest = fileSource.request(Resource::style(url), [this](Response res) {
+ // Once we get a fresh style, or the style is mutated, stop revalidating.
+ if (res.isFresh() || mutated) {
+ styleRequest.reset();
+ }
+
+ // Don't allow a loaded, mutated style to be overwritten with a new version.
+ if (mutated && loaded) {
+ return;
+ }
+
+ if (res.error) {
+ const std::string message = "loading style failed: " + res.error->message;
+ Log::Error(Event::Setup, message.c_str());
+ observer->onStyleError(std::make_exception_ptr(util::StyleLoadException(message)));
+ observer->onResourceError(std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified || res.noContent) {
+ return;
+ } else {
+ parse(*res.data);
+ }
+ });
+}
+
+void Style::Impl::parse(const std::string& json_) {
+ Parser parser;
+
+ if (auto error = parser.parse(json_)) {
+ std::string message = "Failed to parse style: " + util::toString(error);
+ Log::Error(Event::ParseStyle, message.c_str());
+ observer->onStyleError(std::make_exception_ptr(util::StyleParseException(message)));
+ observer->onResourceError(error);
+ return;
+ }
+
+ mutated = false;
+ loaded = false;
+ json = json_;
+
+ sources.clear();
+ layers.clear();
+ images.clear();
+
+ transitionOptions = {};
+ transitionOptions.duration = util::DEFAULT_TRANSITION_DURATION;
+
+ for (auto& source : parser.sources) {
+ addSource(std::move(source));
+ }
+
+ for (auto& layer : parser.layers) {
+ addLayer(std::move(layer));
+ }
+
+ name = parser.name;
+ 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();
+}
+
+std::string Style::Impl::getJSON() const {
+ return json;
+}
+
+std::string Style::Impl::getURL() const {
+ return url;
+}
+
+void Style::Impl::setTransitionOptions(const TransitionOptions& options) {
+ transitionOptions = options;
+}
+
+TransitionOptions Style::Impl::getTransitionOptions() const {
+ return transitionOptions;
+}
+
+void Style::Impl::addSource(std::unique_ptr<Source> source) {
+ if (sources.get(source->getID())) {
+ std::string msg = "Source " + source->getID() + " already exists";
+ throw std::runtime_error(msg.c_str());
+ }
+
+ source->setObserver(this);
+ source->loadDescription(fileSource);
+
+ sources.add(std::move(source));
+}
+
+struct SourceIdUsageEvaluator {
+ const std::string& sourceId;
+
+ bool operator()(BackgroundLayer&) { return false; }
+ bool operator()(CustomLayer&) { return false; }
+
+ template <class LayerType>
+ bool operator()(LayerType& layer) {
+ return layer.getSourceID() == sourceId;
+ }
+};
+
+std::unique_ptr<Source> Style::Impl::removeSource(const std::string& id) {
+ // Check if source is in use
+ SourceIdUsageEvaluator sourceIdEvaluator {id};
+ auto layerIt = std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
+ return layer->accept(sourceIdEvaluator);
+ });
+
+ if (layerIt != layers.end()) {
+ Log::Warning(Event::General, "Source '%s' is in use, cannot remove", id.c_str());
+ return nullptr;
+ }
+
+ std::unique_ptr<Source> source = sources.remove(id);
+
+ if (source) {
+ source->setObserver(nullptr);
+ }
+
+ return source;
+}
+
+std::vector<Layer*> Style::Impl::getLayers() {
+ return layers.getWrappers();
+}
+
+std::vector<const Layer*> Style::Impl::getLayers() const {
+ auto wrappers = layers.getWrappers();
+ return std::vector<const Layer*>(wrappers.begin(), wrappers.end());
+}
+
+Layer* Style::Impl::getLayer(const std::string& id) const {
+ return layers.get(id);
+}
+
+Layer* Style::Impl::addLayer(std::unique_ptr<Layer> layer, optional<std::string> before) {
+ // TODO: verify source
+
+ if (layers.get(layer->getID())) {
+ throw std::runtime_error(std::string{"Layer "} + layer->getID() + " already exists");
+ }
+
+ layer->setObserver(this);
+ observer->onUpdate();
+
+ return layers.add(std::move(layer), before);
+}
+
+std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) {
+ std::unique_ptr<Layer> layer = layers.remove(id);
+
+ if (layer) {
+ layer->setObserver(nullptr);
+ observer->onUpdate();
+ }
+
+ return layer;
+}
+
+void Style::Impl::setLight(std::unique_ptr<Light> light_) {
+ light = std::move(light_);
+ light->setObserver(this);
+ onLightChanged(*light);
+}
+
+Light* Style::Impl::getLight() const {
+ return light.get();
+}
+
+std::string Style::Impl::getName() const {
+ return name;
+}
+
+CameraOptions Style::Impl::getDefaultCamera() const {
+ return defaultCamera;
+}
+
+std::vector<Source*> Style::Impl::getSources() {
+ return sources.getWrappers();
+}
+
+std::vector<const Source*> Style::Impl::getSources() const {
+ auto wrappers = sources.getWrappers();
+ return std::vector<const Source*>(wrappers.begin(), wrappers.end());
+}
+
+Source* Style::Impl::getSource(const std::string& id) const {
+ return sources.get(id);
+}
+
+bool Style::Impl::isLoaded() const {
+ if (!loaded) {
+ return false;
+ }
+
+ if (!spriteLoaded) {
+ return false;
+ }
+
+ for (const auto& source: sources) {
+ if (!source->loaded) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Style::Impl::addImage(std::unique_ptr<style::Image> image) {
+ images.remove(image->getID()); // We permit using addImage to update.
+ images.add(std::move(image));
+}
+
+void Style::Impl::removeImage(const std::string& id) {
+ images.remove(id);
+}
+
+const style::Image* Style::Impl::getImage(const std::string& id) const {
+ return images.get(id);
+}
+
+void Style::Impl::setObserver(style::Observer* observer_) {
+ observer = observer_;
+}
+
+void Style::Impl::onSourceLoaded(Source& source) {
+ sources.update(source);
+ observer->onSourceLoaded(source);
+ observer->onUpdate();
+}
+
+void Style::Impl::onSourceChanged(Source& source) {
+ sources.update(source);
+ observer->onSourceChanged(source);
+ observer->onUpdate();
+}
+
+void Style::Impl::onSourceError(Source& source, std::exception_ptr error) {
+ lastError = error;
+ Log::Error(Event::Style, "Failed to load source %s: %s",
+ source.getID().c_str(), util::toString(error).c_str());
+ observer->onSourceError(source, error);
+ observer->onResourceError(error);
+}
+
+void Style::Impl::onSourceDescriptionChanged(Source& source) {
+ sources.update(source);
+ observer->onSourceDescriptionChanged(source);
+ if (!source.loaded) {
+ source.loadDescription(fileSource);
+ }
+}
+
+void Style::Impl::onSpriteLoaded(std::vector<std::unique_ptr<Image>>&& images_) {
+ for (auto& image : images_) {
+ addImage(std::move(image));
+ }
+ spriteLoaded = true;
+ observer->onUpdate(); // For *-pattern properties.
+}
+
+void Style::Impl::onSpriteError(std::exception_ptr error) {
+ lastError = error;
+ Log::Error(Event::Style, "Failed to load sprite: %s", util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void Style::Impl::onLayerChanged(Layer& layer) {
+ layers.update(layer);
+ observer->onUpdate();
+}
+
+void Style::Impl::onLightChanged(const Light&) {
+ observer->onUpdate();
+}
+
+void Style::Impl::dumpDebugLogs() const {
+ Log::Info(Event::General, "styleURL: %s", url.c_str());
+ for (const auto& source : sources) {
+ source->dumpDebugLogs();
+ }
+}
+
+const std::string& Style::Impl::getGlyphURL() const {
+ return glyphURL;
+}
+
+Immutable<std::vector<Immutable<Image::Impl>>> Style::Impl::getImageImpls() const {
+ return images.getImpls();
+}
+
+Immutable<std::vector<Immutable<Source::Impl>>> Style::Impl::getSourceImpls() const {
+ return sources.getImpls();
+}
+
+Immutable<std::vector<Immutable<Layer::Impl>>> Style::Impl::getLayerImpls() const {
+ return layers.getImpls();
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp
new file mode 100644
index 0000000000..3dc222bfad
--- /dev/null
+++ b/src/mbgl/style/style_impl.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/observer.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/style/layer_observer.hpp>
+#include <mbgl/style/light_observer.hpp>
+#include <mbgl/sprite/sprite_loader_observer.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/source.hpp>
+#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>
+
+#include <memory>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+namespace mbgl {
+
+class Scheduler;
+class FileSource;
+class AsyncRequest;
+class SpriteLoader;
+
+namespace style {
+
+class Style::Impl : public SpriteLoaderObserver,
+ public SourceObserver,
+ public LayerObserver,
+ public LightObserver,
+ public util::noncopyable {
+public:
+ Impl(Scheduler&, FileSource&, float pixelRatio);
+ ~Impl() override;
+
+ void loadJSON(const std::string&);
+ void loadURL(const std::string&);
+
+ std::string getJSON() const;
+ std::string getURL() const;
+
+ void setObserver(Observer*);
+
+ bool isLoaded() const;
+
+ std::exception_ptr getLastError() const {
+ return lastError;
+ }
+
+ std::vector< Source*> getSources();
+ std::vector<const Source*> getSources() const;
+ Source* getSource(const std::string& id) const;
+
+ void addSource(std::unique_ptr<Source>);
+ std::unique_ptr<Source> removeSource(const std::string& sourceID);
+
+ std::vector< Layer*> getLayers();
+ std::vector<const Layer*> getLayers() const;
+ Layer* getLayer(const std::string& id) const;
+
+ Layer* addLayer(std::unique_ptr<Layer>,
+ optional<std::string> beforeLayerID = {});
+ std::unique_ptr<Layer> removeLayer(const std::string& layerID);
+
+ std::string getName() const;
+ CameraOptions getDefaultCamera() const;
+
+ TransitionOptions getTransitionOptions() const;
+ void setTransitionOptions(const TransitionOptions&);
+
+ void setLight(std::unique_ptr<Light>);
+ Light* getLight() const;
+
+ const style::Image* getImage(const std::string&) const;
+ void addImage(std::unique_ptr<style::Image>);
+ void removeImage(const std::string&);
+
+ const std::string& getGlyphURL() const;
+
+ Immutable<std::vector<Immutable<Image::Impl>>> getImageImpls() const;
+ Immutable<std::vector<Immutable<Source::Impl>>> getSourceImpls() const;
+ Immutable<std::vector<Immutable<Layer::Impl>>> getLayerImpls() const;
+
+ void dumpDebugLogs() const;
+
+ bool mutated = false;
+ bool loaded = false;
+ bool spriteLoaded = false;
+
+private:
+ void parse(const std::string&);
+
+ Scheduler& scheduler;
+ FileSource& fileSource;
+
+ std::string url;
+ std::string json;
+
+ std::unique_ptr<AsyncRequest> styleRequest;
+ std::unique_ptr<SpriteLoader> spriteLoader;
+
+ std::string glyphURL;
+ Collection<style::Image> images;
+ Collection<Source> sources;
+ Collection<Layer> layers;
+ TransitionOptions transitionOptions;
+ std::unique_ptr<Light> light;
+
+ // Defaults
+ std::string name;
+ CameraOptions defaultCamera;
+
+ // SpriteLoaderObserver implementation.
+ void onSpriteLoaded(std::vector<std::unique_ptr<Image>>&&) override;
+ void onSpriteError(std::exception_ptr) override;
+
+ // SourceObserver implementation.
+ void onSourceLoaded(Source&) override;
+ void onSourceChanged(Source&) override;
+ void onSourceError(Source&, std::exception_ptr) override;
+ void onSourceDescriptionChanged(Source&) override;
+
+ // LayerObserver implementation.
+ void onLayerChanged(Layer&) override;
+
+ // LightObserver implementation.
+ void onLightChanged(const Light&) override;
+
+ Observer nullObserver;
+ Observer* observer = &nullObserver;
+
+ std::exception_ptr lastError;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/tile_source_impl.cpp b/src/mbgl/style/tile_source_impl.cpp
deleted file mode 100644
index d2ce3def9f..0000000000
--- a/src/mbgl/style/tile_source_impl.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <mbgl/style/tile_source_impl.hpp>
-#include <mbgl/style/source_observer.hpp>
-#include <mbgl/style/conversion/json.hpp>
-#include <mbgl/style/conversion/tileset.hpp>
-#include <mbgl/util/mapbox.hpp>
-#include <mbgl/storage/file_source.hpp>
-
-namespace mbgl {
-namespace style {
-
-TileSourceImpl::TileSourceImpl(SourceType type_, std::string id_, Source& base_,
- variant<std::string, Tileset> urlOrTileset_,
- uint16_t tileSize_)
- : Impl(type_, std::move(id_), base_),
- urlOrTileset(std::move(urlOrTileset_)),
- tileSize(tileSize_) {
-}
-
-TileSourceImpl::~TileSourceImpl() = default;
-
-void TileSourceImpl::loadDescription(FileSource& fileSource) {
- if (urlOrTileset.is<Tileset>()) {
- tileset = urlOrTileset.get<Tileset>();
- loaded = true;
- return;
- }
-
- if (req) {
- return;
- }
-
- const std::string& url = urlOrTileset.get<std::string>();
- req = fileSource.request(Resource::source(url), [this, url](Response res) {
- if (res.error) {
- observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- observer->onSourceError(base, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON")));
- } else {
- conversion::Error error;
- optional<Tileset> newTileset = conversion::convertJSON<Tileset>(*res.data, error);
- if (!newTileset) {
- observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(error.message)));
- return;
- }
-
- util::mapbox::canonicalizeTileset(*newTileset, url, type, tileSize);
- bool attributionChanged = tileset.attribution != (*newTileset).attribution;
-
- tileset = *newTileset;
- loaded = true;
-
- observer->onSourceLoaded(base);
- if (attributionChanged) {
- observer->onSourceChanged(base);
- }
- }
- });
-}
-
-optional<Tileset> TileSourceImpl::getTileset() const {
- if (loaded) {
- return tileset;
- }
- return {};
-}
-
-optional<std::string> TileSourceImpl::getAttribution() const {
- if (loaded && !tileset.attribution.empty()) {
- return tileset.attribution;
- } else {
- return {};
- }
-}
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/tile_source_impl.hpp b/src/mbgl/style/tile_source_impl.hpp
deleted file mode 100644
index 0e5a53add7..0000000000
--- a/src/mbgl/style/tile_source_impl.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include <mbgl/style/source_impl.hpp>
-#include <mbgl/util/tileset.hpp>
-#include <mbgl/util/variant.hpp>
-#include <mbgl/util/optional.hpp>
-
-namespace mbgl {
-
-class AsyncRequest;
-
-namespace style {
-
-/*
- Shared implementation for VectorSource and RasterSource. Should eventually
- be refactored to use composition rather than inheritance.
-*/
-class TileSourceImpl : public Source::Impl {
-public:
- TileSourceImpl(SourceType, std::string id, Source&,
- variant<std::string, Tileset> urlOrTileset,
- uint16_t tileSize);
- ~TileSourceImpl() override;
-
- void loadDescription(FileSource&) final;
-
- uint16_t getTileSize() const {
- return tileSize;
- }
-
- const variant<std::string, Tileset>& getURLOrTileset() const {
- return urlOrTileset;
- }
-
- optional<std::string> getAttribution() const override;
- optional<Tileset> getTileset() const;
-
-protected:
- const variant<std::string, Tileset> urlOrTileset;
- const uint16_t tileSize;
-
- Tileset tileset;
- std::unique_ptr<AsyncRequest> req;
-};
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
index b37e73ffb1..0a1781e01b 100644
--- a/src/mbgl/style/types.cpp
+++ b/src/mbgl/style/types.cpp
@@ -11,6 +11,7 @@ MBGL_DEFINE_ENUM(SourceType, {
{ SourceType::GeoJSON, "geojson" },
{ SourceType::Video, "video" },
{ SourceType::Annotations, "annotations" },
+ { SourceType::Image, "image" },
});
MBGL_DEFINE_ENUM(VisibilityType, {
@@ -52,16 +53,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/style/update_batch.hpp b/src/mbgl/style/update_batch.hpp
deleted file mode 100644
index 562df52afa..0000000000
--- a/src/mbgl/style/update_batch.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include <unordered_set>
-#include <string>
-
-namespace mbgl {
-namespace style {
-
-class UpdateBatch {
-public:
- std::unordered_set<std::string> sourceIDs;
-};
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 885ba5c426..3eb08da8d1 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -42,14 +42,18 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
bboxifyLabel(line, anchorPoint, anchor.segment, length, height);
}
} 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, std::numeric_limits<float>::infinity());
}
}
void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
const int segment, const float labelLength, const float boxSize) {
const float step = boxSize / 2;
- const unsigned int nBoxes = std::floor(labelLength / step);
+ const int nBoxes = std::floor(labelLength / step);
+ // We calculate line collision boxes out to 300% of what would normally be our
+ // max size, to allow collision detection to work on labels that expand as
+ // they move into the distance
+ const int nPitchPaddingBoxes = std::floor(nBoxes / 2);
// 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 +62,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);
- float segmentLength = util::dist<float>(line[index], line[index + 1]);
+ 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) {
@@ -99,11 +125,46 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
p0.y + segmentBoxDistance / segmentLength * (p1.y - p0.y)
};
+ // Distance from label anchor point to inner (towards center) edge of this box
+ // The tricky thing here is that box positioning doesn't change with scale,
+ // but box size does change with scale.
+ // Technically, distanceToInnerEdge should be:
+ // Math.max(Math.abs(boxDistanceToAnchor - firstBoxOffset) - (step / scale), 0);
+ // But using that formula would make solving for maxScale more difficult, so we
+ // approximate with scale=2.
+ // This makes our calculation spot-on at scale=2, and on the conservative side for
+ // lower scales
const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f);
- const float maxScale = labelLength / 2 / distanceToInnerEdge;
+ float maxScale = util::division(labelLength / 2, distanceToInnerEdge, std::numeric_limits<float>::infinity());
+
+ // The box maxScale calculations are designed to be conservative on collisions in the scale range
+ // [1,2]. At scale=1, each box has 50% overlap, and at scale=2, the boxes are lined up edge
+ // to edge (beyond scale 2, gaps start to appear, which could potentially allow missed collisions).
+ // We add "pitch padding" boxes to the left and right to handle effective underzooming
+ // (scale < 1) when labels are in the distance. The overlap approximation could cause us to use
+ // these boxes when the scale is greater than 1, but we prevent that because we know
+ // they're only necessary for scales less than one.
+ // This preserves the pre-pitch-padding behavior for unpitched maps.
+ if (i < 0 || i >= nBoxes) {
+ maxScale = std::min(maxScale, 0.99f);
+ }
- boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale);
+ boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale);
}
}
+float CollisionBox::adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const {
+ // When the map is pitched the distance covered by a line changes.
+ // Adjust the max scale by (approximatePitchedLength / approximateRegularLength)
+ // to compensate for this.
+ const Point<float> rotatedOffset = util::matrixMultiply(rotationMatrix, offset);
+ const float xSqr = rotatedOffset.x * rotatedOffset.x;
+ const float ySqr = rotatedOffset.y * rotatedOffset.y;
+ const float yStretchSqr = ySqr * yStretch * yStretch;
+ const float adjustmentFactor = xSqr + ySqr != 0 ?
+ std::sqrt((xSqr + yStretchSqr) / (xSqr + ySqr)) :
+ 1.0f;
+ return maxScale * adjustmentFactor;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index 006a47eb74..3b6e461a26 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -11,11 +11,16 @@ 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 _maxScale) :
+ anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {}
+
+ float adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const;
// 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;
@@ -34,7 +39,7 @@ public:
class CollisionFeature {
public:
enum class AlignmentType : bool {
- Straight = 0,
+ Straight = false,
Curved
};
diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp
index 368750c89f..cc9b602f08 100644
--- a/src/mbgl/text/collision_tile.cpp
+++ b/src/mbgl/text/collision_tile.cpp
@@ -20,27 +20,39 @@ CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_
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);
+ perspectiveRatio =
+ 1.0f +
+ 0.5f * (util::division(config.cameraToTileDistance, config.cameraToCenterDistance, 1.0f) -
+ 1.0f);
+
+ minScale /= perspectiveRatio;
+ maxScale /= perspectiveRatio;
+
+ // We can only approximate here based on the y position of the tile
+ // The shaders calculate a more accurate "incidence_stretch"
+ // at render time to calculate an effective scale for collision
+ // purposes, but we still want to use the yStretch approximation
+ // here because we can't adjust the aspect ratio of the collision
+ // boxes at render time.
+ yStretch = util::max(
+ 1.0f, util::division(config.cameraToTileDistance,
+ config.cameraToCenterDistance * std::cos(config.pitch), 1.0f));
}
-
-float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking) {
+float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const float boxMaxScale, const Point<float>& blockingAnchor, const CollisionBox& blocking) {
float minPlacementScale = minScale;
// Find the lowest scale at which the two boxes can fit side by side without overlapping.
// Original algorithm:
- 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;
+ const float s1 = util::division(blocking.x1 - box.x2, anchor.x - blockingAnchor.x,
+ 1.0f); // scale at which new box is to the left of old box
+ const float s2 = util::division(blocking.x2 - box.x1, anchor.x - blockingAnchor.x,
+ 1.0f); // scale at which new box is to the right of old box
+ const float s3 = util::division((blocking.y1 - box.y2) * yStretch, anchor.y - blockingAnchor.y,
+ 1.0f); // scale at which new box is to the top of old box
+ const float s4 = util::division((blocking.y2 - box.y1) * yStretch, anchor.y - blockingAnchor.y,
+ 1.0f); // scale at which new box is to the bottom of old box
float collisionFreeScale = util::min(util::max(s1, s2), util::max(s3, s4));
@@ -50,10 +62,10 @@ float CollisionTile::findPlacementScale(const Point<float>& anchor, const Collis
collisionFreeScale = blocking.maxScale;
}
- if (collisionFreeScale > box.maxScale) {
+ if (collisionFreeScale > boxMaxScale) {
// If the box can only be shown after it is visible, then the box can never be shown.
// But the label can be shown after this box is not visible.
- collisionFreeScale = box.maxScale;
+ collisionFreeScale = boxMaxScale;
}
if (collisionFreeScale > minPlacementScale &&
@@ -72,13 +84,13 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve
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),
+ CollisionBox(Point<float>(0, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity),
// right
- CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity),
+ CollisionBox(Point<float>(util::EXTENT, 0), { 0, 0 }, 0, -infinity, 0, infinity, infinity),
// top
- CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity),
+ CollisionBox(Point<float>(0, 0), { 0, 0 }, -infinity, 0, infinity, 0, infinity),
// bottom
- CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity)
+ CollisionBox(Point<float>(0, util::EXTENT), { 0, 0 }, -infinity, 0, infinity, 0, infinity)
}};
float minPlacementScale = minScale;
@@ -86,12 +98,14 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve
for (auto& box : feature.boxes) {
const auto anchor = util::matrixMultiply(rotationMatrix, box.anchor);
+ const float boxMaxScale = box.adjustedMaxScale(rotationMatrix, yStretch);
+
if (!allowOverlap) {
for (auto it = tree.qbegin(bgi::intersects(getTreeBox(anchor, box))); it != tree.qend(); ++it) {
const CollisionBox& blocking = std::get<1>(*it);
Point<float> blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor);
- minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, blockingAnchor, blocking));
+ minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, boxMaxScale, blockingAnchor, blocking));
if (minPlacementScale >= maxScale) return minPlacementScale;
}
}
@@ -102,14 +116,15 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOve
const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y2 });
const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y2 });
CollisionBox rotatedBox(box.anchor,
+ box.offset,
util::min(rtl.x, rtr.x, rbl.x, rbr.x),
util::min(rtl.y, rtr.y, rbl.y, rbr.y),
util::max(rtl.x, rtr.x, rbl.x, rbr.x),
util::max(rtl.y, rtr.y, rbl.y, rbr.y),
- box.maxScale);
+ boxMaxScale);
for (auto& blocking : edges) {
- minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, blocking.anchor, blocking));
+ minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, boxMaxScale, blocking.anchor, blocking));
if (minPlacementScale >= maxScale) return minPlacementScale;
}
}
@@ -126,7 +141,9 @@ void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementS
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);
+ CollisionBox adjustedBox = box;
+ box.maxScale = box.adjustedMaxScale(rotationMatrix, yStretch);
+ treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), std::move(adjustedBox), feature.indexedFeature);
}
if (ignorePlacement) {
ignoredTree.insert(treeBoxes.begin(), treeBoxes.end());
@@ -157,13 +174,21 @@ void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementS
Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale) {
assert(box.x1 <= box.x2 && box.y1 <= box.y2);
return Box{
+ // When the 'perspectiveRatio' is high, we're effectively underzooming
+ // the tile because it's in the distance.
+ // In order to detect collisions that only happen while underzoomed,
+ // we have to query a larger portion of the grid.
+ // This extra work is offset by having a lower 'maxScale' bound
+ // Note that this adjustment ONLY affects the bounding boxes
+ // in the grid. It doesn't affect the boxes used for the
+ // minPlacementScale calculations.
CollisionPoint{
- anchor.x + box.x1 / scale,
- anchor.y + box.y1 / scale * yStretch
+ anchor.x + box.x1 / scale * perspectiveRatio,
+ anchor.y + box.y1 / scale * yStretch * perspectiveRatio,
},
CollisionPoint{
- anchor.x + box.x2 / scale,
- anchor.y + box.y2 / scale * yStretch
+ anchor.x + box.x2 / scale * perspectiveRatio,
+ anchor.y + box.y2 / scale * yStretch * perspectiveRatio
}
};
}
@@ -190,23 +215,30 @@ std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const Geometr
return seenFeatures.find(feature.index) == seenFeatures.end();
};
+ // "perspectiveRatio" is a tile-based approximation of how much larger symbols will
+ // be in the distance. It won't line up exactly with the actually rendered symbols
+ // Being exact would require running the collision detection logic in symbol_sdf.vertex
+ // in the CPU
+ const float perspectiveScale = scale / perspectiveRatio;
+
// Account for the rounding done when updating symbol shader variables.
- const float roundedScale = std::pow(2.0f, std::ceil(util::log2(scale) * 10.0f) / 10.0f);
+ const float roundedScale = std::pow(2.0f, std::ceil(util::log2(perspectiveScale) * 10.0f) / 10.0f);
// Check if feature is rendered (collision free) at current scale.
auto visibleAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
const CollisionBox& box = std::get<1>(treeBox);
- return roundedScale >= box.placementScale && roundedScale <= box.maxScale;
+ return roundedScale >= box.placementScale && roundedScale <= box.adjustedMaxScale(rotationMatrix, yStretch);
};
// Check if query polygon intersects with the feature box at current scale.
auto intersectsAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
const CollisionBox& collisionBox = std::get<1>(treeBox);
const auto anchor = util::matrixMultiply(rotationMatrix, collisionBox.anchor);
- const int16_t x1 = anchor.x + collisionBox.x1 / 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;
+
+ const int16_t x1 = anchor.x + (collisionBox.x1 / perspectiveScale);
+ const int16_t y1 = anchor.y + (collisionBox.y1 / perspectiveScale) * yStretch;
+ const int16_t x2 = anchor.x + (collisionBox.x2 / perspectiveScale);
+ const int16_t y2 = anchor.y + (collisionBox.y2 / perspectiveScale) * yStretch;
auto bbox = GeometryCoordinates {
{ x1, y1 }, { x2, y1 }, { x2, y2 }, { x1, y2 }
};
diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp
index 4508e13a4b..9868266aa2 100644
--- a/src/mbgl/text/collision_tile.hpp
+++ b/src/mbgl/text/collision_tile.hpp
@@ -31,10 +31,10 @@ namespace mbgl {
namespace bg = boost::geometry;
namespace bgm = bg::model;
namespace bgi = bg::index;
-typedef bgm::point<float, 2, bg::cs::cartesian> CollisionPoint;
-typedef bgm::box<CollisionPoint> Box;
-typedef std::tuple<Box, CollisionBox, IndexedSubfeature> CollisionTreeBox;
-typedef bgi::rtree<CollisionTreeBox, bgi::linear<16, 4>> Tree;
+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;
@@ -49,8 +49,8 @@ public:
const PlacementConfig config;
- const float minScale = 0.5f;
- const float maxScale = 2.0f;
+ float minScale = 0.5f;
+ float maxScale = 2.0f;
float yStretch;
std::array<float, 4> rotationMatrix;
@@ -58,12 +58,14 @@ public:
private:
float findPlacementScale(
- const Point<float>& anchor, const CollisionBox& box,
+ const Point<float>& anchor, const CollisionBox& box, const float boxMaxScale,
const Point<float>& blockingAnchor, const CollisionBox& blocking);
Box getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale = 1.0);
Tree tree;
Tree ignoredTree;
+
+ float perspectiveRatio;
};
} // namespace mbgl
diff --git a/src/mbgl/text/get_anchors.cpp b/src/mbgl/text/get_anchors.cpp
index 82702b20f0..d41faf2a71 100644
--- a/src/mbgl/text/get_anchors.cpp
+++ b/src/mbgl/text/get_anchors.cpp
@@ -34,7 +34,7 @@ static Anchors resample(const GeometryCoordinates& line,
const GeometryCoordinate& a = *(it);
const GeometryCoordinate& b = *(it + 1);
- const float segmentDist = util::dist<float>(a, b);
+ const auto segmentDist = util::dist<float>(a, b);
const float angle = util::angle_to(b, a);
while (markedDistance + spacing < distance + segmentDist) {
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 9cf39de840..6cccb72ebe 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -5,6 +5,9 @@
#include <mbgl/util/rect.hpp>
#include <mbgl/util/traits.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/immutable.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/util.hpp>
#include <cstdint>
#include <vector>
@@ -13,8 +16,8 @@
namespace mbgl {
-typedef char16_t GlyphID;
-typedef std::set<GlyphID> GlyphIDs;
+using GlyphID = char16_t;
+using GlyphIDs = std::set<GlyphID>;
// Note: this only works for the BMP
GlyphRange getGlyphRange(GlyphID glyph);
@@ -35,37 +38,47 @@ inline bool operator==(const GlyphMetrics& lhs, const GlyphMetrics& rhs) {
lhs.advance == rhs.advance;
}
-struct Glyph {
- Rect<uint16_t> rect;
+class Glyph {
+public:
+ // We're using this value throughout the Mapbox GL ecosystem. If this is different, the glyphs
+ // also need to be reencoded.
+ static constexpr const uint8_t borderSize = 3;
+
+ GlyphID id = 0;
+
+ // A signed distance field of the glyph with a border (see above).
+ AlphaImage bitmap;
+
+ // Glyph metrics
GlyphMetrics metrics;
};
-typedef std::map<GlyphID, optional<Glyph>> GlyphPositions;
-typedef std::map<FontStack, GlyphPositions> GlyphPositionMap;
+using Glyphs = std::map<GlyphID, optional<Immutable<Glyph>>>;
+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;
class Shaping {
public:
- explicit Shaping() : top(0), bottom(0), left(0), right(0) {}
+ explicit Shaping() = default;
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;
- int32_t bottom;
- int32_t left;
- int32_t right;
+ int32_t top = 0;
+ int32_t bottom = 0;
+ int32_t left = 0;
+ int32_t right = 0;
WritingModeType writingMode;
explicit operator bool() const { return !positionedGlyphs.empty(); }
@@ -77,28 +90,27 @@ 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));
}
-typedef std::map<FontStack,GlyphIDs> GlyphDependencies;
-typedef std::map<FontStack,GlyphRangeSet> GlyphRangeDependencies;
-
+using GlyphDependencies = std::map<FontStack,GlyphIDs>;
+using GlyphRangeDependencies = std::map<FontStack,GlyphRangeSet>;
} // end namespace mbgl
diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp
index 4feaab01f9..1b98ea36bf 100644
--- a/src/mbgl/text/glyph_atlas.cpp
+++ b/src/mbgl/text/glyph_atlas.cpp
@@ -1,262 +1,65 @@
#include <mbgl/text/glyph_atlas.hpp>
-#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/text/glyph_pbf.hpp>
-#include <mbgl/gl/context.hpp>
-#include <mbgl/util/logging.hpp>
-#include <mbgl/util/platform.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <cassert>
-#include <algorithm>
+#include <mapbox/shelf-pack.hpp>
namespace mbgl {
-static GlyphAtlasObserver nullObserver;
+static constexpr uint32_t padding = 1;
-GlyphAtlas::GlyphAtlas(const Size size, FileSource& fileSource_)
- : fileSource(fileSource_),
- observer(&nullObserver),
- bin(size.width, size.height),
- image(size),
- dirty(true) {
-}
-
-GlyphAtlas::~GlyphAtlas() = default;
-
-void GlyphAtlas::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) {
- auto dependencies = std::make_shared<GlyphDependencies>(std::move(glyphDependencies));
-
- // Figure out which glyph ranges need to be fetched. For each range that does need to
- // be fetched, record an entry mapping the requestor to a shared pointer containing the
- // dependencies. When the shared pointer becomes unique, we know that all the dependencies
- // for that requestor have been fetched, and can notify it of completion.
- for (const auto& dependency : *dependencies) {
- const FontStack& fontStack = dependency.first;
- Entry& entry = entries[fontStack];
-
- const GlyphIDs& glyphIDs = dependency.second;
- GlyphRangeSet ranges;
- for (const auto& glyphID : glyphIDs) {
- 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);
- request.requestors[&requestor] = dependencies;
- }
- }
- }
-
- // If the shared dependencies pointer is already unique, then all dependent glyph ranges
- // have already been loaded. Send a notification immediately.
- if (dependencies.unique()) {
- addGlyphs(requestor, *dependencies);
- }
-}
-
-GlyphAtlas::GlyphRequest& GlyphAtlas::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
- GlyphRequest& request = entry.ranges[range];
-
- if (request.req) {
- return request;
- }
-
- request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) {
- processResponse(res, fontStack, range);
- });
+GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) {
+ GlyphAtlas result;
- return request;
-}
+ mapbox::ShelfPack::ShelfPackOptions options;
+ options.autoResize = true;
+ mapbox::ShelfPack pack(0, 0, options);
-void GlyphAtlas::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) {
- if (res.error) {
- observer->onGlyphsError(fontStack, range, std::make_exception_ptr(std::runtime_error(res.error->message)));
- return;
- }
+ for (const auto& glyphMapEntry : glyphs) {
+ const FontStack& fontStack = glyphMapEntry.first;
+ GlyphPositionMap& positions = result.positions[fontStack];
- if (res.notModified) {
- return;
- }
+ for (const auto& entry : glyphMapEntry.second) {
+ if (entry.second && (*entry.second)->bitmap.valid()) {
+ const Glyph& glyph = **entry.second;
- Entry& entry = entries[fontStack];
- GlyphRequest& request = entry.ranges[range];
+ const mapbox::Bin& bin = *pack.packOne(-1,
+ glyph.bitmap.size.width + 2 * padding,
+ glyph.bitmap.size.height + 2 * padding);
- if (!res.noContent) {
- std::vector<SDFGlyph> glyphs;
-
- try {
- glyphs = parseGlyphPBF(range, *res.data);
- } catch (...) {
- observer->onGlyphsError(fontStack, range, std::current_exception());
- return;
- }
-
- for (auto& glyph : glyphs) {
- auto it = entry.glyphs.find(glyph.id);
- if (it == entry.glyphs.end()) {
- // Glyph doesn't exist yet.
- entry.glyphs.emplace(glyph.id, GlyphValue {
- std::move(glyph.bitmap),
- std::move(glyph.metrics),
- {}, {}
+ result.image.resize({
+ static_cast<uint32_t>(pack.width()),
+ static_cast<uint32_t>(pack.height())
});
- } else if (it->second.metrics == glyph.metrics) {
- if (it->second.bitmap != glyph.bitmap) {
- // The actual bitmap was updated; this is unsupported.
- Log::Warning(Event::Glyph, "Modified glyph changed bitmap represenation");
- }
- // At least try to update it in case it's currently unused.
- // If it is already used, we won't attempt to update the glyph atlas texture.
- it->second.bitmap = std::move(glyph.bitmap);
- } else {
- // The metrics were updated; this is unsupported.
- Log::Warning(Event::Glyph, "Modified glyph has different metrics");
- return;
- }
- }
- }
-
- request.parsed = true;
-
- for (auto& pair : request.requestors) {
- GlyphRequestor& requestor = *pair.first;
- const std::shared_ptr<GlyphDependencies>& dependencies = pair.second;
- if (dependencies.unique()) {
- addGlyphs(requestor, *dependencies);
- }
- }
-
- request.requestors.clear();
-
- observer->onGlyphsLoaded(fontStack, range);
-}
-
-void GlyphAtlas::setObserver(GlyphAtlasObserver* observer_) {
- observer = observer_ ? observer_ : &nullObserver;
-}
-void GlyphAtlas::addGlyphs(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) {
- GlyphPositionMap glyphPositions;
-
- for (const auto& dependency : glyphDependencies) {
- const FontStack& fontStack = dependency.first;
- const GlyphIDs& glyphIDs = dependency.second;
-
- GlyphPositions& positions = glyphPositions[fontStack];
- Entry& entry = entries[fontStack];
-
- for (const auto& glyphID : glyphIDs) {
- // Make a glyph position entry even if we didn't get an SDF for the glyph. During layout,
- // an empty optional is treated as "loaded but nothing to show", wheras no entry in the
- // positions map means "not loaded yet".
- optional<Glyph>& glyph = positions[glyphID];
-
- auto it = entry.glyphs.find(glyphID);
- if (it == entry.glyphs.end())
- continue;
-
- it->second.ids.insert(&requestor);
-
- glyph = Glyph {
- addGlyph(it->second),
- it->second.metrics
- };
- }
- }
-
- requestor.onGlyphsAvailable(glyphPositions);
-}
-
-Rect<uint16_t> GlyphAtlas::addGlyph(GlyphValue& value) {
- // The glyph is already in this texture.
- if (value.rect) {
- return *value.rect;
- }
-
- // We don't need to add glyphs without a bitmap (e.g. whitespace).
- if (!value.bitmap.valid()) {
- return {};
- }
-
- // Add a 1px border around every image.
- const uint32_t padding = 1;
- uint16_t width = value.bitmap.size.width + 2 * padding;
- uint16_t height = value.bitmap.size.height + 2 * padding;
-
- // Increase to next number divisible by 4, but at least 1.
- // This is so we can scale down the texture coordinates and pack them
- // into 2 bytes rather than 4 bytes.
- width += (4 - width % 4);
- height += (4 - height % 4);
-
- Rect<uint16_t> rect = bin.allocate(width, height);
- if (rect.w == 0) {
- Log::Error(Event::OpenGL, "glyph bitmap overflow");
- return {};
- }
-
- AlphaImage::copy(value.bitmap, image, { 0, 0 }, { rect.x + padding, rect.y + padding }, value.bitmap.size);
- value.rect = rect;
- dirty = true;
-
- return rect;
-}
-
-void GlyphAtlas::removeGlyphValues(GlyphRequestor& requestor, std::map<GlyphID, GlyphValue>& values) {
- for (auto it = values.begin(); it != values.end(); it++) {
- GlyphValue& value = it->second;
- if (value.ids.erase(&requestor) && value.ids.empty() && value.rect) {
- const Rect<uint16_t>& rect = *value.rect;
-
- // Clear out the bitmap.
- uint8_t *target = image.data.get();
- for (uint32_t y = 0; y < rect.h; y++) {
- uint32_t y1 = image.size.width * (rect.y + y) + rect.x;
- for (uint32_t x = 0; x < rect.w; x++) {
- target[y1 + x] = 0;
- }
+ AlphaImage::copy(glyph.bitmap,
+ result.image,
+ { 0, 0 },
+ {
+ bin.x + padding,
+ bin.y + padding
+ },
+ glyph.bitmap.size);
+
+ positions.emplace(glyph.id,
+ GlyphPosition {
+ Rect<uint16_t> {
+ static_cast<uint16_t>(bin.x),
+ static_cast<uint16_t>(bin.y),
+ static_cast<uint16_t>(bin.w),
+ static_cast<uint16_t>(bin.h)
+ },
+ glyph.metrics
+ });
}
-
- bin.release(rect);
- value.rect = {};
}
}
-}
-
-void GlyphAtlas::removePendingRanges(mbgl::GlyphRequestor& requestor, std::map<GlyphRange, GlyphRequest>& ranges) {
- for (auto it = ranges.begin(); it != ranges.end(); it++) {
- it->second.requestors.erase(&requestor);
- }
-}
-void GlyphAtlas::removeGlyphs(GlyphRequestor& requestor) {
- for (auto& entry : entries) {
- removeGlyphValues(requestor, entry.second.glyphs);
- removePendingRanges(requestor, entry.second.ranges);
- }
-}
-
-Size GlyphAtlas::getSize() const {
- return image.size;
-}
-
-void GlyphAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
- if (!texture) {
- texture = context.createTexture(image, unit);
- } else if (dirty) {
- context.updateTexture(*texture, image, unit);
- }
-
- dirty = false;
-}
+ pack.shrink();
+ result.image.resize({
+ static_cast<uint32_t>(pack.width()),
+ static_cast<uint32_t>(pack.height())
+ });
-void GlyphAtlas::bind(gl::Context& context, gl::TextureUnit unit) {
- upload(context, unit);
- context.bindTexture(*texture, unit, gl::TextureFilter::Linear);
+ return result;
}
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp
index ad9cf35adc..bb9115e4b4 100644
--- a/src/mbgl/text/glyph_atlas.hpp
+++ b/src/mbgl/text/glyph_atlas.hpp
@@ -1,110 +1,25 @@
#pragma once
#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/text/glyph_range.hpp>
-#include <mbgl/geometry/binpack.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/font_stack.hpp>
-#include <mbgl/util/work_queue.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/gl/texture.hpp>
-#include <mbgl/gl/object.hpp>
-#include <string>
-#include <unordered_set>
-#include <unordered_map>
-
-class GlyphAtlasTest;
+#include <mapbox/shelf-pack.hpp>
namespace mbgl {
-class FileSource;
-class AsyncRequest;
-class Response;
-
-namespace gl {
-class Context;
-} // namespace gl
-
-class GlyphRequestor {
-public:
- virtual ~GlyphRequestor() = default;
- virtual void onGlyphsAvailable(GlyphPositionMap) = 0;
+struct GlyphPosition {
+ Rect<uint16_t> rect;
+ GlyphMetrics metrics;
};
-
-class GlyphAtlas : public util::noncopyable {
-public:
- GlyphAtlas(Size, FileSource&);
- ~GlyphAtlas();
-
- // Workers send a `getGlyphs` message to the main thread once they have determined
- // which glyphs they will need. Invoking this method will increment reference
- // counts for all the glyphs in `GlyphDependencies`. If all glyphs are already
- // locally available, the observer will be notified that the glyphs are available
- // immediately. Otherwise, a request on the FileSource is made, and when all glyphs
- // are parsed and added to the atlas, the observer will be notified.
- // Workers are given a copied 'GlyphPositions' map to use for placing their glyphs.
- // The positions specified in this object are guaranteed to be
- // valid for the lifetime of the tile.
- void getGlyphs(GlyphRequestor&, GlyphDependencies);
- void removeGlyphs(GlyphRequestor&);
-
- void setURL(const std::string& url) {
- glyphURL = url;
- }
-
- void setObserver(GlyphAtlasObserver*);
-
- // Binds the atlas texture to the GPU, and uploads data if it is out of date.
- void bind(gl::Context&, gl::TextureUnit unit);
-
- // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
- // the texture is only bound when the data is out of date (=dirty).
- void upload(gl::Context&, gl::TextureUnit unit);
-
- Size getSize() const;
-private:
- FileSource& fileSource;
- std::string glyphURL;
+using GlyphPositionMap = std::map<GlyphID, GlyphPosition>;
+using GlyphPositions = std::map<FontStack, GlyphPositionMap>;
- struct GlyphValue {
- AlphaImage bitmap;
- GlyphMetrics metrics;
- optional<Rect<uint16_t>> rect;
- std::unordered_set<GlyphRequestor*> ids;
- };
-
- struct GlyphRequest {
- bool parsed = false;
- std::unique_ptr<AsyncRequest> req;
- std::unordered_map<GlyphRequestor*, std::shared_ptr<GlyphDependencies>> requestors;
- };
-
- struct Entry {
- std::map<GlyphRange, GlyphRequest> ranges;
- std::map<GlyphID, GlyphValue> glyphs;
- };
-
- std::unordered_map<FontStack, Entry, FontStackHash> entries;
-
- GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&);
- void processResponse(const Response&, const FontStack&, const GlyphRange&);
-
- void addGlyphs(GlyphRequestor&, const GlyphDependencies&);
- Rect<uint16_t> addGlyph(GlyphValue&);
-
- void removeGlyphValues(GlyphRequestor&, std::map<GlyphID, GlyphValue>&);
- void removePendingRanges(GlyphRequestor&, std::map<GlyphRange, GlyphRequest>&);
-
- GlyphAtlasObserver* observer = nullptr;
-
- BinPack<uint16_t> bin;
+class GlyphAtlas {
+public:
AlphaImage image;
- bool dirty;
- mbgl::optional<gl::Texture> texture;
+ GlyphPositions positions;
};
+GlyphAtlas makeGlyphAtlas(const GlyphMap&);
+
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp
new file mode 100644
index 0000000000..916d39ae62
--- /dev/null
+++ b/src/mbgl/text/glyph_manager.cpp
@@ -0,0 +1,145 @@
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/text/glyph_pbf.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+
+namespace mbgl {
+
+static GlyphManagerObserver nullObserver;
+
+GlyphManager::GlyphManager(FileSource& fileSource_)
+ : fileSource(fileSource_),
+ observer(&nullObserver) {
+}
+
+GlyphManager::~GlyphManager() = default;
+
+void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) {
+ auto dependencies = std::make_shared<GlyphDependencies>(std::move(glyphDependencies));
+
+ // Figure out which glyph ranges need to be fetched. For each range that does need to
+ // be fetched, record an entry mapping the requestor to a shared pointer containing the
+ // dependencies. When the shared pointer becomes unique, we know that all the dependencies
+ // for that requestor have been fetched, and can notify it of completion.
+ for (const auto& dependency : *dependencies) {
+ const FontStack& fontStack = dependency.first;
+ Entry& entry = entries[fontStack];
+
+ const GlyphIDs& glyphIDs = dependency.second;
+ GlyphRangeSet ranges;
+ for (const auto& glyphID : glyphIDs) {
+ 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);
+ request.requestors[&requestor] = dependencies;
+ }
+ }
+ }
+
+ // If the shared dependencies pointer is already unique, then all dependent glyph ranges
+ // have already been loaded. Send a notification immediately.
+ if (dependencies.unique()) {
+ notify(requestor, *dependencies);
+ }
+}
+
+GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
+ GlyphRequest& request = entry.ranges[range];
+
+ if (request.req) {
+ return request;
+ }
+
+ 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) {
+ if (res.error) {
+ observer->onGlyphsError(fontStack, range, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ return;
+ }
+
+ if (res.notModified) {
+ return;
+ }
+
+ Entry& entry = entries[fontStack];
+ GlyphRequest& request = entry.ranges[range];
+
+ if (!res.noContent) {
+ std::vector<Glyph> glyphs;
+
+ try {
+ glyphs = parseGlyphPBF(range, *res.data);
+ } catch (...) {
+ observer->onGlyphsError(fontStack, range, std::current_exception());
+ return;
+ }
+
+ for (auto& glyph : glyphs) {
+ entry.glyphs.erase(glyph.id);
+ entry.glyphs.emplace(glyph.id, makeMutable<Glyph>(std::move(glyph)));
+ }
+ }
+
+ request.parsed = true;
+
+ for (auto& pair : request.requestors) {
+ GlyphRequestor& requestor = *pair.first;
+ const std::shared_ptr<GlyphDependencies>& dependencies = pair.second;
+ if (dependencies.unique()) {
+ notify(requestor, *dependencies);
+ }
+ }
+
+ request.requestors.clear();
+
+ observer->onGlyphsLoaded(fontStack, range);
+}
+
+void GlyphManager::setObserver(GlyphManagerObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver;
+}
+
+void GlyphManager::notify(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) {
+ GlyphMap response;
+
+ for (const auto& dependency : glyphDependencies) {
+ const FontStack& fontStack = dependency.first;
+ const GlyphIDs& glyphIDs = dependency.second;
+
+ Glyphs& glyphs = response[fontStack];
+ Entry& entry = entries[fontStack];
+
+ for (const auto& glyphID : glyphIDs) {
+ auto it = entry.glyphs.find(glyphID);
+ if (it != entry.glyphs.end()) {
+ glyphs.emplace(*it);
+ } else {
+ glyphs.emplace(glyphID, std::experimental::nullopt);
+ }
+ }
+ }
+
+ requestor.onGlyphsAvailable(response);
+}
+
+void GlyphManager::removeRequestor(GlyphRequestor& requestor) {
+ for (auto& entry : entries) {
+ for (auto& range : entry.second.ranges) {
+ range.second.requestors.erase(&requestor);
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp
new file mode 100644
index 0000000000..00df079462
--- /dev/null
+++ b/src/mbgl/text/glyph_manager.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/text/glyph_range.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/font_stack.hpp>
+#include <mbgl/util/immutable.hpp>
+
+#include <string>
+#include <unordered_map>
+
+namespace mbgl {
+
+class FileSource;
+class AsyncRequest;
+class Response;
+
+class GlyphRequestor {
+public:
+ virtual ~GlyphRequestor() = default;
+ virtual void onGlyphsAvailable(GlyphMap) = 0;
+};
+
+class GlyphManager : public util::noncopyable {
+public:
+ GlyphManager(FileSource&);
+ ~GlyphManager();
+
+ // Workers send a `getGlyphs` message to the main thread once they have determined
+ // their `GlyphDependencies`. If all glyphs are already locally available, GlyphManager
+ // will provide them to the requestor immediately. Otherwise, it makes a request on the
+ // FileSource is made for each range neeed, and notifies the observer when all are
+ // complete.
+ void getGlyphs(GlyphRequestor&, GlyphDependencies);
+ void removeRequestor(GlyphRequestor&);
+
+ void setURL(const std::string& url) {
+ glyphURL = url;
+ }
+
+ void setObserver(GlyphManagerObserver*);
+
+private:
+ FileSource& fileSource;
+ std::string glyphURL;
+
+ struct GlyphRequest {
+ bool parsed = false;
+ std::unique_ptr<AsyncRequest> req;
+ std::unordered_map<GlyphRequestor*, std::shared_ptr<GlyphDependencies>> requestors;
+ };
+
+ struct Entry {
+ std::map<GlyphRange, GlyphRequest> ranges;
+ std::map<GlyphID, Immutable<Glyph>> glyphs;
+ };
+
+ std::unordered_map<FontStack, Entry, FontStackHash> entries;
+
+ GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&);
+ void processResponse(const Response&, const FontStack&, const GlyphRange&);
+ void notify(GlyphRequestor&, const GlyphDependencies&);
+
+ GlyphManagerObserver* observer = nullptr;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph_atlas_observer.hpp b/src/mbgl/text/glyph_manager_observer.hpp
index 9841017117..b8678e060a 100644
--- a/src/mbgl/text/glyph_atlas_observer.hpp
+++ b/src/mbgl/text/glyph_manager_observer.hpp
@@ -8,9 +8,9 @@
namespace mbgl {
-class GlyphAtlasObserver {
+class GlyphManagerObserver {
public:
- virtual ~GlyphAtlasObserver() = default;
+ virtual ~GlyphManagerObserver() = default;
virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&) {}
virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) {}
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
index 033f50fe9c..cfaf803f75 100644
--- a/src/mbgl/text/glyph_pbf.cpp
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -4,8 +4,8 @@
namespace mbgl {
-std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) {
- std::vector<SDFGlyph> result;
+std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) {
+ std::vector<Glyph> result;
result.reserve(256);
protozero::pbf_reader glyphs_pbf(data);
@@ -15,7 +15,7 @@ std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::str
while (fontstack_pbf.next(3)) {
auto glyph_pbf = fontstack_pbf.get_message();
- SDFGlyph glyph;
+ Glyph glyph;
protozero::data_view glyphData;
bool hasID = false, hasWidth = false, hasHeight = false, hasLeft = false,
@@ -73,8 +73,8 @@ std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::str
// with the implicit border size, otherwise we expect there to be no bitmap at all.
if (glyph.metrics.width && glyph.metrics.height) {
const Size size {
- glyph.metrics.width + 2 * SDFGlyph::borderSize,
- glyph.metrics.height + 2 * SDFGlyph::borderSize
+ glyph.metrics.width + 2 * Glyph::borderSize,
+ glyph.metrics.height + 2 * Glyph::borderSize
};
if (size.area() != glyphData.size()) {
diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp
index 162aeed93a..28a28b4114 100644
--- a/src/mbgl/text/glyph_pbf.hpp
+++ b/src/mbgl/text/glyph_pbf.hpp
@@ -2,28 +2,12 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/text/glyph_range.hpp>
-#include <mbgl/util/image.hpp>
#include <string>
#include <vector>
namespace mbgl {
-class SDFGlyph {
-public:
- // We're using this value throughout the Mapbox GL ecosystem. If this is different, the glyphs
- // also need to be reencoded.
- static constexpr const uint8_t borderSize = 3;
-
- GlyphID id = 0;
-
- // A signed distance field of the glyph with a border (see above).
- AlphaImage bitmap;
-
- // Glyph metrics
- GlyphMetrics metrics;
-};
-
-std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange&, const std::string& data);
+std::vector<Glyph> parseGlyphPBF(const GlyphRange&, const std::string& data);
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_range.hpp b/src/mbgl/text/glyph_range.hpp
index dd39e092b7..74afb73dfc 100644
--- a/src/mbgl/text/glyph_range.hpp
+++ b/src/mbgl/text/glyph_range.hpp
@@ -7,7 +7,7 @@
namespace mbgl {
-typedef std::pair<uint16_t, uint16_t> GlyphRange;
+using GlyphRange = std::pair<uint16_t, uint16_t>;
struct GlyphRangeHash {
std::size_t operator()(const GlyphRange& glyphRange) const {
@@ -15,7 +15,7 @@ struct GlyphRangeHash {
}
};
-typedef std::unordered_set<GlyphRange, GlyphRangeHash> GlyphRangeSet;
+using GlyphRangeSet = std::unordered_set<GlyphRange, GlyphRangeHash>;
constexpr uint32_t GLYPHS_PER_GLYPH_RANGE = 256;
constexpr uint32_t GLYPH_RANGES_PER_FONT_STACK = 256;
diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp
index 7e61cabc24..48b24b5f41 100644
--- a/src/mbgl/text/placement_config.hpp
+++ b/src/mbgl/text/placement_config.hpp
@@ -1,15 +1,21 @@
#pragma once
+#include <mbgl/util/constants.hpp>
+
namespace mbgl {
class PlacementConfig {
public:
- PlacementConfig(float angle_ = 0, float pitch_ = 0, bool debug_ = false)
- : angle(angle_), pitch(pitch_), debug(debug_) {
+ PlacementConfig(float angle_ = 0, float pitch_ = 0, float cameraToCenterDistance_ = 0, float cameraToTileDistance_ = 0, bool debug_ = false)
+ : angle(angle_), pitch(pitch_), cameraToCenterDistance(cameraToCenterDistance_), cameraToTileDistance(cameraToTileDistance_), debug(debug_) {
}
bool operator==(const PlacementConfig& rhs) const {
- return angle == rhs.angle && pitch == rhs.pitch && debug == rhs.debug;
+ return angle == rhs.angle &&
+ pitch == rhs.pitch &&
+ debug == rhs.debug &&
+ ((pitch * util::RAD2DEG < 25) ||
+ (cameraToCenterDistance == rhs.cameraToCenterDistance && cameraToTileDistance == rhs.cameraToTileDistance));
}
bool operator!=(const PlacementConfig& rhs) const {
@@ -19,6 +25,8 @@ public:
public:
float angle;
float pitch;
+ float cameraToCenterDistance;
+ float cameraToTileDistance;
bool debug;
};
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index e1a9699835..0014ae8d01 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -13,22 +13,21 @@ 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) {
- auto image = *shapedIcon.image();
+ const ImagePosition& image = shapedIcon.image();
+ // If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual
+ // pixels. The quad needs to be padded to account for this, otherwise they'll look slightly clipped
+ // on one edge in some cases.
const float border = 1.0;
- auto left = shapedIcon.left() - border;
- auto right = left + image.pos.w / image.relativePixelRatio;
- auto top = shapedIcon.top() - border;
- auto bottom = top + image.pos.h / image.relativePixelRatio;
+
+ float top = shapedIcon.top() - border / image.pixelRatio;
+ float left = shapedIcon.left() - border / image.pixelRatio;
+ float bottom = shapedIcon.bottom() + border / image.pixelRatio;
+ float right = shapedIcon.right() + border / image.pixelRatio;
Point<float> tl;
Point<float> tr;
Point<float> br;
@@ -67,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.
@@ -92,283 +80,95 @@ SymbolQuad getIconQuad(const Anchor& anchor,
br = util::matrixMultiply(matrix, br);
}
- return SymbolQuad { tl, tr, bl, br, image.pos, 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;
-};
-
-typedef std::vector<GlyphInstance> GlyphInstances;
-
-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 float 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()
+ // Icon quad is padded, so texture coordinates also need to be padded.
+ Rect<uint16_t> textureRect {
+ static_cast<uint16_t>(image.textureRect.x - border),
+ static_cast<uint16_t>(image.textureRect.y - border),
+ static_cast<uint16_t>(image.textureRect.w + border * 2),
+ static_cast<uint16_t>(image.textureRect.h + border * 2)
};
-
- 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 GlyphPositions& face) {
+ 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;
for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) {
- auto face_it = face.find(positionedGlyph.glyph);
- if (face_it == face.end() || !face_it->second || !(*face_it->second).rect.hasArea())
+ auto positionsIt = positions.find(positionedGlyph.glyph);
+ if (positionsIt == positions.end())
continue;
- const Glyph& glyph = *face_it->second;
+ 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 333000627b..33d003c935 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
@@ -19,52 +19,35 @@ 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;
};
-typedef std::vector<SymbolQuad> SymbolQuads;
+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 GlyphPositions& face);
+ const GlyphPositionMap& positions);
} // namespace mbgl
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 78aa142c61..5d688ea539 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -7,22 +7,70 @@
#include <boost/algorithm/string.hpp>
#include <algorithm>
+#include <cmath>
namespace mbgl {
-optional<PositionedIcon> PositionedIcon::shapeIcon(const SpriteAtlasElement& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
- if (!image.pos.hasArea()) {
- return {};
+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.width/ 2.0f;
- float x2 = x1 + image.width;
- float y1 = dy - image.height / 2.0f;
- float y2 = y1 + image.height;
+ float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
+ float x2 = x1 + image.displaySize()[0];
+ float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
+ float y2 = y1 + image.displaySize()[1];
- return { PositionedIcon { image, y1, y2, x1, x2, iconRotation } };
+ return PositionedIcon { image, y1, y2, x1, x2, iconRotation };
}
void align(Shaping& shaping,
@@ -31,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;
@@ -46,7 +91,7 @@ void align(Shaping& shaping,
// justify left = 0, right = 1, center = .5
void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
- const GlyphPositions& glyphs,
+ const Glyphs& glyphs,
std::size_t start,
std::size_t end,
float justify) {
@@ -57,7 +102,7 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
PositionedGlyph& glyph = positionedGlyphs[end];
auto it = glyphs.find(glyph.glyph);
if (it != glyphs.end() && it->second) {
- const uint32_t lastAdvance = it->second->metrics.advance;
+ const uint32_t lastAdvance = (*it->second)->metrics.advance;
const float lineIndent = float(glyph.x + lastAdvance) * justify;
for (std::size_t j = start; j <= end; j++) {
@@ -69,17 +114,17 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
float determineAverageLineWidth(const std::u16string& logicalInput,
const float spacing,
float maxWidth,
- const GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
float totalWidth = 0;
for (char16_t chr : logicalInput) {
auto it = glyphs.find(chr);
if (it != glyphs.end() && it->second) {
- totalWidth += it->second->metrics.advance + spacing;
+ totalWidth += (*it->second)->metrics.advance + spacing;
}
}
- int32_t targetLineCount = std::fmax(1, std::ceil(totalWidth / maxWidth));
+ int32_t targetLineCount = ::fmax(1, std::ceil(totalWidth / maxWidth));
return totalWidth / targetLineCount;
}
@@ -168,7 +213,7 @@ std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput,
const float spacing,
float maxWidth,
const WritingModeType writingMode,
- const GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
if (!maxWidth || writingMode != WritingModeType::Horizontal) {
return {};
}
@@ -186,7 +231,7 @@ std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput,
const char16_t codePoint = logicalInput[i];
auto it = glyphs.find(codePoint);
if (it != glyphs.end() && it->second && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) {
- currentX += it->second->metrics.advance + spacing;
+ currentX += (*it->second)->metrics.advance + spacing;
}
// Ideographic characters, spaces, and word-breaking punctuation that often appear without
@@ -206,13 +251,11 @@ 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 GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
// the y offset *should* be part of the font metadata
const int32_t yOffset = -17;
@@ -221,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
@@ -238,13 +285,13 @@ void shapeLines(Shaping& shaping,
continue;
}
- const Glyph& glyph = *it->second;
+ 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;
}
}
@@ -261,38 +308,39 @@ void shapeLines(Shaping& shaping,
x = 0;
y += lineHeight;
}
-
- align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight,
- lines.size(), translate);
+
+ auto anchorAlign = getAnchorAlignment(textAnchor);
+
+ align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
+ lineHeight, lines.size());
const uint32_t 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,
const WritingModeType writingMode,
BiDi& bidi,
- const GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
Shaping shaping(translate.x, translate.y, writingMode);
std::vector<std::u16string> reorderedLines =
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 b7eee5a5db..0a961849e5 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -1,32 +1,30 @@
#pragma once
#include <mbgl/text/glyph.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/style/image.hpp>
-#include <mbgl/util/optional.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
+#include <mbgl/style/types.hpp>
namespace mbgl {
-class SpriteAtlasElement;
class SymbolFeature;
class BiDi;
class PositionedIcon {
private:
- PositionedIcon(const SpriteAtlasElement& image_,
+ PositionedIcon(ImagePosition image_,
float top_,
float bottom_,
float left_,
float right_,
float angle_)
- : _image(image_),
+ : _image(std::move(image_)),
_top(top_),
_bottom(bottom_),
_left(left_),
_right(right_),
_angle(angle_) {}
- optional<SpriteAtlasElement> _image;
+ ImagePosition _image;
float _top;
float _bottom;
float _left;
@@ -34,9 +32,12 @@ private:
float _angle;
public:
- static optional<PositionedIcon> shapeIcon(const class SpriteAtlasElement&, 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);
- optional<class SpriteAtlasElement> image() const { return _image; }
+ const ImagePosition& image() const { return _image; }
float top() const { return _top; }
float bottom() const { return _bottom; }
float left() const { return _left; }
@@ -47,14 +48,13 @@ 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,
const WritingModeType,
BiDi& bidi,
- const GlyphPositions& glyphs);
+ const Glyphs& glyphs);
} // namespace mbgl
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index 3c4939a0a6..5d8339d775 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -1,8 +1,9 @@
#include <mbgl/tile/geojson_tile.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/map/query.hpp>
-#include <mbgl/style/style.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
#include <supercluster.hpp>
@@ -52,43 +53,57 @@ public:
}
};
-class GeoJSONTileData : public GeometryTileData,
- public GeometryTileLayer {
+class GeoJSONTileLayer : public GeometryTileLayer {
public:
- mapbox::geometry::feature_collection<int16_t> features;
-
- GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_)
+ GeoJSONTileLayer(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>(*this);
+ std::size_t featureCount() const override {
+ return features->size();
}
- const GeometryTileLayer* getLayer(const std::string&) const override {
- return this;
+ std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
+ return std::make_unique<GeoJSONTileFeature>((*features)[i]);
}
std::string getName() const override {
return "";
}
- std::size_t featureCount() const override {
- return features.size();
+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_))) {
}
- std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
- return std::make_unique<GeoJSONTileFeature>(features[i]);
+ 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,
mapbox::geometry::feature_collection<int16_t> features)
- : GeometryTile(overscaledTileID, sourceID_, parameters,
- *parameters.style.glyphAtlas,
- *parameters.style.spriteAtlas) {
+ : GeometryTile(overscaledTileID, sourceID_, parameters) {
updateData(std::move(features));
}
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 23b3737a38..8c018ce3aa 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -6,20 +6,20 @@
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/renderer/render_background_layer.hpp>
-#include <mbgl/renderer/render_custom_layer.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
-#include <mbgl/style/style.hpp>
+#include <mbgl/renderer/layers/render_background_layer.hpp>
+#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>
@@ -27,32 +27,51 @@ 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', 'setPlacementConfig') 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,
- GlyphAtlas& glyphAtlas_,
- SpriteAtlas& spriteAtlas_)
+ const TileParameters& parameters)
: Tile(id_),
sourceID(std::move(sourceID_)),
- style(parameters.style),
- mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(parameters.workerScheduler,
ActorRef<GeometryTile>(*this, mailbox),
id_,
obsolete,
- parameters.mode),
- glyphAtlas(glyphAtlas_),
- spriteAtlas(spriteAtlas_),
- placementThrottler(Milliseconds(300), [this] { invokePlacement(); }) {
+ parameters.mode,
+ parameters.pixelRatio),
+ glyphManager(parameters.glyphManager),
+ imageManager(parameters.imageManager),
+ lastYStretch(1.0f),
+ mode(parameters.mode) {
}
GeometryTile::~GeometryTile() {
- glyphAtlas.removeGlyphs(*this);
- spriteAtlas.removeRequestor(*this);
- cancel();
+ glyphManager.removeRequestor(*this);
+ imageManager.removeRequestor(*this);
+ markObsolete();
}
void GeometryTile::cancel() {
+ markObsolete();
+}
+
+void GeometryTile::markObsolete() {
obsolete = true;
}
@@ -68,7 +87,6 @@ void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
++correlationID;
worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID);
- redoLayout();
}
void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
@@ -82,7 +100,7 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
++correlationID;
requestedConfig = desiredConfig;
- placementThrottler.invoke();
+ invokePlacement();
}
void GeometryTile::invokePlacement() {
@@ -91,29 +109,29 @@ void GeometryTile::invokePlacement() {
}
}
-void GeometryTile::redoLayout() {
+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
// state despite pending parse operations.
pending = true;
- std::vector<std::unique_ptr<Layer>> copy;
+ std::vector<Immutable<Layer::Impl>> impls;
- for (const Layer* layer : style.getLayers()) {
- // Avoid cloning and including irrelevant layers.
- if (layer->is<BackgroundLayer>() ||
- layer->is<CustomLayer>() ||
- layer->baseImpl->source != sourceID ||
- id.overscaledZ < std::floor(layer->baseImpl->minZoom) ||
- id.overscaledZ >= std::ceil(layer->baseImpl->maxZoom) ||
- layer->baseImpl->visibility == VisibilityType::None) {
+ for (const auto& layer : layers) {
+ // Skip irrelevant layers.
+ if (layer->type == LayerType::Background ||
+ layer->type == LayerType::Custom ||
+ layer->source != sourceID ||
+ id.overscaledZ < std::floor(layer->minZoom) ||
+ id.overscaledZ >= std::ceil(layer->maxZoom) ||
+ layer->visibility == VisibilityType::None) {
continue;
}
- copy.push_back(layer->baseImpl->clone());
+ impls.push_back(layer);
}
++correlationID;
- worker.invoke(&GeometryTileWorker::setLayers, std::move(copy), correlationID);
+ worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID);
}
void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
@@ -134,10 +152,16 @@ void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorr
pending = false;
}
symbolBuckets = std::move(result.symbolBuckets);
- for (auto& entry : symbolBuckets) {
- dynamic_cast<SymbolBucket*>(entry.second.get())->spriteAtlas = &spriteAtlas;
- }
collisionTile = std::move(result.collisionTile);
+ if (result.glyphAtlasImage) {
+ glyphAtlasImage = std::move(*result.glyphAtlasImage);
+ }
+ if (result.iconAtlasImage) {
+ iconAtlasImage = std::move(*result.iconAtlasImage);
+ }
+ if (collisionTile.get()) {
+ lastYStretch = collisionTile->yStretch;
+ }
observer->onTileChanged(*this);
}
@@ -149,25 +173,51 @@ void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelat
observer->onTileError(*this, err);
}
-void GeometryTile::onGlyphsAvailable(GlyphPositionMap glyphPositions) {
- worker.invoke(&GeometryTileWorker::onGlyphsAvailable, std::move(glyphPositions));
+void GeometryTile::onGlyphsAvailable(GlyphMap glyphs) {
+ worker.invoke(&GeometryTileWorker::onGlyphsAvailable, std::move(glyphs));
}
void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) {
- glyphAtlas.getGlyphs(*this, std::move(glyphDependencies));
+ glyphManager.getGlyphs(*this, std::move(glyphDependencies));
}
-void GeometryTile::onIconsAvailable(IconMap icons) {
- worker.invoke(&GeometryTileWorker::onIconsAvailable, std::move(icons));
+void GeometryTile::onImagesAvailable(ImageMap images, uint64_t imageCorrelationID) {
+ worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), imageCorrelationID);
}
-void GeometryTile::getIcons(IconDependencies) {
- spriteAtlas.getIcons(*this);
+void GeometryTile::getImages(ImageRequestPair pair) {
+ imageManager.getImages(*this, std::move(pair));
}
-Bucket* GeometryTile::getBucket(const RenderLayer& layer) const {
- const auto& buckets = layer.is<RenderSymbolLayer>() ? symbolBuckets : nonSymbolBuckets;
- const auto it = buckets.find(layer.baseImpl.id);
+void GeometryTile::upload(gl::Context& context) {
+ auto uploadFn = [&] (Bucket& bucket) {
+ if (bucket.needsUpload()) {
+ bucket.upload(context);
+ }
+ };
+
+ for (auto& entry : nonSymbolBuckets) {
+ uploadFn(*entry.second);
+ }
+
+ for (auto& entry : symbolBuckets) {
+ uploadFn(*entry.second);
+ }
+
+ if (glyphAtlasImage) {
+ glyphAtlasTexture = context.createTexture(*glyphAtlasImage, 0);
+ glyphAtlasImage = {};
+ }
+
+ if (iconAtlasImage) {
+ iconAtlasTexture = context.createTexture(*iconAtlasImage, 0);
+ iconAtlasImage = {};
+ }
+}
+
+Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const {
+ const auto& buckets = layer.type == LayerType::Symbol ? symbolBuckets : nonSymbolBuckets;
+ const auto it = buckets.find(layer.id);
if (it == buckets.end()) {
return nullptr;
}
@@ -180,10 +230,20 @@ void GeometryTile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) {
if (!featureIndex || !data) return;
+ // Determine the additional radius needed factoring in property functions
+ float additionalRadius = 0;
+ 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(),
@@ -192,9 +252,9 @@ void GeometryTile::queryRenderedFeatures(
options,
*data,
id.canonical,
- style,
+ layers,
collisionTile.get(),
- *this);
+ additionalRadius);
}
void GeometryTile::querySourceFeatures(
@@ -233,4 +293,11 @@ void GeometryTile::querySourceFeatures(
}
}
+float GeometryTile::yStretch() const {
+ // collisionTile gets reset in onLayout but we don't clear the symbolBuckets
+ // until a new placement result comes along, so keep the yStretch value in
+ // case we need to render them.
+ return lastYStretch;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index 910cb446a0..a478aad504 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -1,13 +1,15 @@
#pragma once
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/geometry_tile_worker.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/text/placement_config.hpp>
+#include <mbgl/text/collision_tile.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/throttler.hpp>
#include <mbgl/actor/actor.hpp>
+#include <mbgl/geometry/feature_index.hpp>
#include <atomic>
#include <memory>
@@ -17,23 +19,17 @@
namespace mbgl {
class GeometryTileData;
-class FeatureIndex;
-class CollisionTile;
class RenderLayer;
class SourceQueryOptions;
class TileParameters;
+class GlyphAtlas;
+class ImageAtlas;
-namespace style {
-class Style;
-} // namespace style
-
-class GeometryTile : public Tile, public GlyphRequestor, IconRequestor {
+class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor {
public:
GeometryTile(const OverscaledTileID&,
std::string sourceID,
- const TileParameters&,
- GlyphAtlas&,
- SpriteAtlas&);
+ const TileParameters&);
~GeometryTile() override;
@@ -41,20 +37,25 @@ public:
void setData(std::unique_ptr<const GeometryTileData>);
void setPlacementConfig(const PlacementConfig&) override;
- void redoLayout() override;
+ void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override;
- void onGlyphsAvailable(GlyphPositionMap) override;
- void onIconsAvailable(IconMap) override;
+ void onGlyphsAvailable(GlyphMap) override;
+ void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override;
void getGlyphs(GlyphDependencies);
- void getIcons(IconDependencies);
+ void getImages(ImageRequestPair);
+
+ void upload(gl::Context&) override;
+ Bucket* getBucket(const style::Layer::Impl&) const override;
- Bucket* getBucket(const RenderLayer&) const override;
+ Size bindGlyphAtlas(gl::Context&);
+ Size bindIconAtlas(gl::Context&);
void queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState&,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) override;
void querySourceFeatures(
@@ -68,6 +69,13 @@ public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<GeometryTileData> tileData;
+
+ LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_,
+ std::unique_ptr<FeatureIndex> featureIndex_,
+ std::unique_ptr<GeometryTileData> tileData_)
+ : nonSymbolBuckets(std::move(nonSymbolBuckets_)),
+ featureIndex(std::move(featureIndex_)),
+ tileData(std::move(tileData_)) {}
};
void onLayout(LayoutResult, uint64_t correlationID);
@@ -75,21 +83,34 @@ public:
public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
std::unique_ptr<CollisionTile> collisionTile;
+ optional<AlphaImage> glyphAtlasImage;
+ optional<PremultipliedImage> iconAtlasImage;
+
+ PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
+ std::unique_ptr<CollisionTile> collisionTile_,
+ optional<AlphaImage> glyphAtlasImage_,
+ optional<PremultipliedImage> iconAtlasImage_)
+ : symbolBuckets(std::move(symbolBuckets_)),
+ collisionTile(std::move(collisionTile_)),
+ glyphAtlasImage(std::move(glyphAtlasImage_)),
+ iconAtlasImage(std::move(iconAtlasImage_)) {}
};
void onPlacement(PlacementResult, uint64_t correlationID);
void onError(std::exception_ptr, uint64_t correlationID);
+ float yStretch() const override;
+
protected:
const GeometryTileData* getData() {
return data.get();
}
private:
+ void markObsolete();
void invokePlacement();
-
+
const std::string sourceID;
- style::Style& style;
// Used to signal the worker that it should abandon parsing this tile as soon as possible.
std::atomic<bool> obsolete { false };
@@ -97,8 +118,8 @@ private:
std::shared_ptr<Mailbox> mailbox;
Actor<GeometryTileWorker> worker;
- GlyphAtlas& glyphAtlas;
- SpriteAtlas& spriteAtlas;
+ GlyphManager& glyphManager;
+ ImageManager& imageManager;
uint64_t correlationID = 0;
optional<PlacementConfig> requestedConfig;
@@ -107,10 +128,18 @@ private:
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<const GeometryTileData> data;
+ optional<AlphaImage> glyphAtlasImage;
+ optional<PremultipliedImage> iconAtlasImage;
+
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
std::unique_ptr<CollisionTile> collisionTile;
-
- util::Throttler placementThrottler;
+
+ float lastYStretch;
+ const MapMode mode;
+
+public:
+ optional<gl::Texture> glyphAtlasTexture;
+ optional<gl::Texture> iconAtlasTexture;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_data.hpp b/src/mbgl/tile/geometry_tile_data.hpp
index 285f86cc7b..449d8cab28 100644
--- a/src/mbgl/tile/geometry_tile_data.hpp
+++ b/src/mbgl/tile/geometry_tile_data.hpp
@@ -51,7 +51,11 @@ class GeometryTileLayer {
public:
virtual ~GeometryTileLayer() = default;
virtual std::size_t featureCount() const = 0;
+
+ // Returns the feature object at the given position within the layer. The returned feature
+ // object may *not* outlive the layer object.
virtual std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const = 0;
+
virtual std::string getName() const = 0;
};
@@ -59,7 +63,10 @@ class GeometryTileData {
public:
virtual ~GeometryTileData() = default;
virtual std::unique_ptr<GeometryTileData> clone() const = 0;
- virtual const GeometryTileLayer* getLayer(const std::string&) const = 0;
+
+ // Returns the layer with the given name. The returned layer object *may* outlive the data
+ // object.
+ virtual std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const = 0;
};
// classifies an array of rings into polygons with outer rings and holes
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index 25834914c4..50429420c3 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -3,14 +3,13 @@
#include <mbgl/tile/geometry_tile.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/layout/symbol_layout.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/group_by_layout.hpp>
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/renderer/render_symbol_layer.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/string.hpp>
@@ -26,16 +25,17 @@ GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
ActorRef<GeometryTile> parent_,
OverscaledTileID id_,
const std::atomic<bool>& obsolete_,
- const MapMode mode_)
+ const MapMode mode_,
+ const float pixelRatio_)
: self(std::move(self_)),
parent(std::move(parent_)),
id(std::move(id_)),
obsolete(obsolete_),
- mode(mode_) {
+ mode(mode_),
+ pixelRatio(pixelRatio_) {
}
-GeometryTileWorker::~GeometryTileWorker() {
-}
+GeometryTileWorker::~GeometryTileWorker() = default;
/*
GeometryTileWorker is a state machine. This is its transition diagram.
@@ -92,7 +92,7 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_,
}
}
-void GeometryTileWorker::setLayers(std::vector<std::unique_ptr<Layer>> layers_, uint64_t correlationID_) {
+void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_, uint64_t correlationID_) {
try {
layers = std::move(layers_);
correlationID = correlationID_;
@@ -144,14 +144,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;
@@ -196,37 +196,40 @@ void GeometryTileWorker::coalesce() {
self.invoke(&GeometryTileWorker::coalesced);
}
-void GeometryTileWorker::onGlyphsAvailable(GlyphPositionMap newGlyphPositions) {
- for (auto& newFontGlyphs : newGlyphPositions) {
+void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) {
+ for (auto& newFontGlyphs : newGlyphMap) {
const FontStack& fontStack = newFontGlyphs.first;
- GlyphPositions& newPositions = newFontGlyphs.second;
+ Glyphs& newGlyphs = newFontGlyphs.second;
- GlyphPositions& positions = glyphPositions[fontStack];
+ Glyphs& glyphs = glyphMap[fontStack];
GlyphIDs& pendingGlyphIDs = pendingGlyphDependencies[fontStack];
- for (auto& newPosition : newPositions) {
- const GlyphID& glyphID = newPosition.first;
- optional<Glyph>& glyph = newPosition.second;
+ for (auto& newGlyph : newGlyphs) {
+ const GlyphID& glyphID = newGlyph.first;
+ optional<Immutable<Glyph>>& glyph = newGlyph.second;
if (pendingGlyphIDs.erase(glyphID)) {
- positions.emplace(glyphID, std::move(glyph));
+ glyphs.emplace(glyphID, std::move(glyph));
}
}
}
symbolDependenciesChanged();
}
-void GeometryTileWorker::onIconsAvailable(IconMap newIcons) {
- icons = std::move(newIcons);
- pendingIconDependencies.clear();
+void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap, uint64_t imageCorrelationID_) {
+ if (imageCorrelationID != imageCorrelationID_) {
+ return; // Ignore outdated image request replies.
+ }
+ imageMap = std::move(newImageMap);
+ pendingImageDependencies.clear();
symbolDependenciesChanged();
}
void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependencies) {
for (auto& fontDependencies : glyphDependencies) {
- auto fontGlyphs = glyphPositions.find(fontDependencies.first);
+ auto fontGlyphs = glyphMap.find(fontDependencies.first);
for (auto glyphID : fontDependencies.second) {
- if (fontGlyphs == glyphPositions.end() || fontGlyphs->second.find(glyphID) == fontGlyphs->second.end()) {
+ if (fontGlyphs == glyphMap.end() || fontGlyphs->second.find(glyphID) == fontGlyphs->second.end()) {
pendingGlyphDependencies[fontDependencies.first].insert(glyphID);
}
}
@@ -236,21 +239,20 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen
}
}
-void GeometryTileWorker::requestNewIcons(const IconDependencies& iconDependencies) {
- pendingIconDependencies = iconDependencies;
- if (!pendingIconDependencies.empty()) {
- parent.invoke(&GeometryTile::getIcons, pendingIconDependencies);
+void GeometryTileWorker::requestNewImages(const ImageDependencies& imageDependencies) {
+ pendingImageDependencies = imageDependencies;
+ if (!pendingImageDependencies.empty()) {
+ parent.invoke(&GeometryTile::getImages, std::make_pair(pendingImageDependencies, ++imageCorrelationID));
}
}
-static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vector<std::unique_ptr<style::Layer>>& layers, float zoom) {
+static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vector<Immutable<style::Layer::Impl>>& layers, float zoom) {
std::vector<std::unique_ptr<RenderLayer>> renderLayers;
renderLayers.reserve(layers.size());
for (auto& layer : layers) {
- renderLayers.push_back(layer->baseImpl->createRenderLayer());
+ renderLayers.push_back(RenderLayer::create(layer));
- renderLayers.back()->cascade(CascadeParameters {
- { ClassID::Default },
+ renderLayers.back()->transition(TransitionParameters {
Clock::time_point::max(),
TransitionOptions()
});
@@ -269,18 +271,18 @@ void GeometryTileWorker::redoLayout() {
std::vector<std::string> symbolOrder;
for (auto it = layers->rbegin(); it != layers->rend(); it++) {
- if ((*it)->is<SymbolLayer>()) {
- symbolOrder.push_back((*it)->getID());
+ if ((*it)->type == LayerType::Symbol) {
+ symbolOrder.push_back((*it)->id);
}
}
std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap;
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
auto featureIndex = std::make_unique<FeatureIndex>();
- BucketParameters parameters { id, mode };
+ BucketParameters parameters { id, mode, pixelRatio };
GlyphDependencies glyphDependencies;
- IconDependencies iconDependencies;
+ ImageDependencies imageDependencies;
// Create render layers and group by layout
std::vector<std::unique_ptr<RenderLayer>> renderLayers = toRenderLayers(*layers, id.overscaledZ);
@@ -297,7 +299,7 @@ void GeometryTileWorker::redoLayout() {
const RenderLayer& leader = *group.at(0);
- auto geometryLayer = (*data)->getLayer(leader.baseImpl.sourceLayer);
+ auto geometryLayer = (*data)->getLayer(leader.baseImpl->sourceLayer);
if (!geometryLayer) {
continue;
}
@@ -310,11 +312,13 @@ void GeometryTileWorker::redoLayout() {
featureIndex->setBucketLayerIDs(leader.getID(), layerIDs);
if (leader.is<RenderSymbolLayer>()) {
- symbolLayoutMap.emplace(leader.getID(),
- leader.as<RenderSymbolLayer>()->createLayout(parameters, group, *geometryLayer, glyphDependencies, iconDependencies));
+ 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;
+ const Filter& filter = leader.baseImpl->filter;
+ const std::string& sourceLayerID = leader.baseImpl->sourceLayer;
std::shared_ptr<Bucket> bucket = leader.createBucket(parameters, group);
for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) {
@@ -347,7 +351,7 @@ void GeometryTileWorker::redoLayout() {
}
requestNewGlyphs(glyphDependencies);
- requestNewIcons(iconDependencies);
+ requestNewImages(imageDependencies);
parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult {
std::move(buckets),
@@ -358,31 +362,42 @@ void GeometryTileWorker::redoLayout() {
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()) {
return true;
}
}
- return !pendingIconDependencies.empty();
+ return !pendingImageDependencies.empty();
}
-
void GeometryTileWorker::attemptPlacement() {
if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) {
return;
}
+ optional<AlphaImage> glyphAtlasImage;
+ optional<PremultipliedImage> iconAtlasImage;
+
+ if (symbolLayoutsNeedPreparation) {
+ GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap);
+ ImageAtlas imageAtlas = makeImageAtlas(imageMap);
+
+ glyphAtlasImage = std::move(glyphAtlas.image);
+ iconAtlasImage = std::move(imageAtlas.image);
+
+ for (auto& symbolLayout : symbolLayouts) {
+ if (obsolete) {
+ return;
+ }
+
+ symbolLayout->prepare(glyphMap, glyphAtlas.positions,
+ imageMap, imageAtlas.positions);
+ }
+
+ symbolLayoutsNeedPreparation = false;
+ }
+
auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
@@ -391,11 +406,6 @@ void GeometryTileWorker::attemptPlacement() {
return;
}
- if (symbolLayout->state == SymbolLayout::Pending) {
- symbolLayout->prepare(glyphPositions, icons);
- symbolLayout->state = SymbolLayout::Placed;
- }
-
if (!symbolLayout->hasSymbolInstances()) {
continue;
}
@@ -408,7 +418,9 @@ void GeometryTileWorker::attemptPlacement() {
parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
std::move(buckets),
- std::move(collisionTile),
+ std::move(collisionTile),
+ std::move(glyphAtlasImage),
+ std::move(iconAtlasImage),
}, correlationID);
}
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 1df1ef43c4..1425daa7a1 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -2,11 +2,13 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/tile/tile_id.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/style/image_impl.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/text/placement_config.hpp>
#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/immutable.hpp>
+#include <mbgl/style/layer_impl.hpp>
#include <atomic>
#include <memory>
@@ -15,9 +17,7 @@ namespace mbgl {
class GeometryTile;
class GeometryTileData;
-class GlyphAtlas;
class SymbolLayout;
-class RenderLayer;
namespace style {
class Layer;
@@ -29,15 +29,16 @@ public:
ActorRef<GeometryTile> parent,
OverscaledTileID,
const std::atomic<bool>&,
- const MapMode);
+ const MapMode,
+ const float pixelRatio);
~GeometryTileWorker();
- void setLayers(std::vector<std::unique_ptr<style::Layer>>, uint64_t correlationID);
+ 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 onGlyphsAvailable(GlyphPositionMap glyphs);
- void onIconsAvailable(IconMap icons);
+ void onGlyphsAvailable(GlyphMap glyphs);
+ void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID);
private:
void coalesced();
@@ -47,11 +48,10 @@ private:
void coalesce();
void requestNewGlyphs(const GlyphDependencies&);
- void requestNewIcons(const IconDependencies&);
+ void requestNewImages(const ImageDependencies&);
void symbolDependenciesChanged();
bool hasPendingSymbolDependencies() const;
- bool hasPendingSymbolLayouts() const;
ActorRef<GeometryTileWorker> self;
ActorRef<GeometryTile> parent;
@@ -59,6 +59,7 @@ private:
const OverscaledTileID id;
const std::atomic<bool>& obsolete;
const MapMode mode;
+ const float pixelRatio;
enum State {
Idle,
@@ -69,17 +70,19 @@ 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<std::unique_ptr<style::Layer>>> layers;
+ 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;
- IconDependencies pendingIconDependencies;
- GlyphPositionMap glyphPositions;
- IconMap icons;
+ ImageDependencies pendingImageDependencies;
+ GlyphMap glyphMap;
+ ImageMap imageMap;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp
index 12dfaf87d4..2a3c9eeb0e 100644
--- a/src/mbgl/tile/raster_tile.cpp
+++ b/src/mbgl/tile/raster_tile.cpp
@@ -7,8 +7,8 @@
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/renderer/raster_bucket.hpp>
-#include <mbgl/util/run_loop.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.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)) {
}
@@ -43,7 +43,7 @@ void RasterTile::setData(std::shared_ptr<const std::string> data,
worker.invoke(&RasterTileWorker::parse, data, correlationID);
}
-void RasterTile::onParsed(std::unique_ptr<Bucket> result, const uint64_t resultCorrelationID) {
+void RasterTile::onParsed(std::unique_ptr<RasterBucket> result, const uint64_t resultCorrelationID) {
bucket = std::move(result);
loaded = true;
if (resultCorrelationID == correlationID) {
@@ -61,10 +61,22 @@ void RasterTile::onError(std::exception_ptr err, const uint64_t resultCorrelatio
observer->onTileError(*this, err);
}
-Bucket* RasterTile::getBucket(const RenderLayer&) const {
+void RasterTile::upload(gl::Context& context) {
+ if (bucket) {
+ bucket->upload(context);
+ }
+}
+
+Bucket* RasterTile::getBucket(const style::Layer::Impl&) const {
return bucket.get();
}
+void RasterTile::setMask(TileMask&& mask) {
+ if (bucket) {
+ bucket->setMask(std::move(mask));
+ }
+}
+
void RasterTile::setNecessity(Necessity necessity) {
loader.setNecessity(necessity);
}
diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp
index 28fcb554a9..2cb64e8ed7 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;
@@ -29,9 +30,13 @@ public:
optional<Timestamp> expires_);
void cancel() override;
- Bucket* getBucket(const RenderLayer&) const override;
- void onParsed(std::unique_ptr<Bucket> result, uint64_t correlationID);
+ void upload(gl::Context&) override;
+ Bucket* getBucket(const style::Layer::Impl&) const override;
+
+ void setMask(TileMask&&) override;
+
+ void onParsed(std::unique_ptr<RasterBucket> result, uint64_t correlationID);
void onError(std::exception_ptr, uint64_t correlationID);
private:
@@ -44,7 +49,7 @@ private:
// 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 585fe5ec95..4afa876429 100644
--- a/src/mbgl/tile/raster_tile_worker.cpp
+++ b/src/mbgl/tile/raster_tile_worker.cpp
@@ -1,6 +1,6 @@
#include <mbgl/tile/raster_tile_worker.hpp>
#include <mbgl/tile/raster_tile.hpp>
-#include <mbgl/renderer/raster_bucket.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
#include <mbgl/actor/actor.hpp>
#include <mbgl/util/premultiply.hpp>
@@ -17,7 +17,7 @@ void RasterTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t c
}
try {
- auto bucket = std::make_unique<RasterBucket>(util::unpremultiply(decodeImage(*data)));
+ auto bucket = std::make_unique<RasterBucket>(decodeImage(*data));
parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID);
} catch (...) {
parent.invoke(&RasterTile::onError, std::current_exception(), correlationID);
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index 0adc151a64..7d7eb0b3fc 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/debug_bucket.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 {
@@ -33,6 +33,7 @@ void Tile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>&,
const GeometryCoordinates&,
const TransformState&,
+ const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&) {}
void Tile::querySourceFeatures(
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 795fd62140..39cc0de8bd 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -6,9 +6,11 @@
#include <mbgl/util/feature.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/storage/resource.hpp>
+#include <mbgl/style/layer_impl.hpp>
#include <string>
#include <memory>
@@ -21,12 +23,13 @@ class DebugBucket;
class TransformState;
class TileObserver;
class PlacementConfig;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
-class RenderLayer;
-namespace style {
-} // namespace style
+namespace gl {
+class Context;
+} // namespace gl
class Tile : private util::noncopyable {
public:
@@ -47,15 +50,18 @@ public:
// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel() = 0;
- virtual Bucket* getBucket(const RenderLayer&) const = 0;
+ virtual void upload(gl::Context&) = 0;
+ virtual Bucket* getBucket(const style::Layer::Impl&) const = 0;
virtual void setPlacementConfig(const PlacementConfig&) {}
- virtual void redoLayout() {}
+ 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 std::vector<const RenderLayer*>&,
const RenderedQueryOptions& options);
virtual void querySourceFeatures(
@@ -101,6 +107,8 @@ public:
// Contains the tile ID string for painting debug information.
std::unique_ptr<DebugBucket> debugBucket;
+
+ virtual float yStretch() const { return 1.0f; }
protected:
bool triedOptional = false;
diff --git a/src/mbgl/tile/tile_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_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp
index 899cbaf9b0..598ec32c10 100644
--- a/src/mbgl/tile/tile_loader_impl.hpp
+++ b/src/mbgl/tile/tile_loader_impl.hpp
@@ -61,7 +61,10 @@ void TileLoader<T>::loadOptional() {
// 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.priorModified = res.modified;
resource.priorExpires = Timestamp{ Seconds::zero() };
+ resource.priorEtag = res.etag;
+ resource.priorData = res.data;
} else {
loadedData(res);
}
diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp
index 46914e5f5a..e2e700b7b7 100644
--- a/src/mbgl/tile/vector_tile.cpp
+++ b/src/mbgl/tile/vector_tile.cpp
@@ -1,94 +1,15 @@
#include <mbgl/tile/vector_tile.hpp>
+#include <mbgl/tile/vector_tile_data.hpp>
#include <mbgl/tile/tile_loader_impl.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/style/style.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-#include <protozero/pbf_reader.hpp>
-
-#include <unordered_map>
-#include <functional>
-#include <utility>
-
namespace mbgl {
-class VectorTileLayer;
-
-using packed_iter_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>;
-
-struct VectorTileLayerData {
- VectorTileLayerData(std::shared_ptr<const std::string>);
-
- // Hold a reference to the underlying pbf data that backs the lazily-built
- // components of the owning VectorTileLayer and VectorTileFeature objects
- std::shared_ptr<const std::string> data;
-
- uint32_t version = 1;
- uint32_t extent = 4096;
- std::unordered_map<std::string, uint32_t> keysMap;
- std::vector<std::reference_wrapper<const std::string>> keys;
- std::vector<Value> values;
-};
-
-class VectorTileFeature : public GeometryTileFeature {
-public:
- VectorTileFeature(protozero::pbf_reader, std::shared_ptr<VectorTileLayerData> layerData);
-
- FeatureType getType() const override { return type; }
- optional<Value> getValue(const std::string&) const override;
- std::unordered_map<std::string,Value> getProperties() const override;
- optional<FeatureIdentifier> getID() const override;
- GeometryCollection getGeometries() const override;
-
-private:
- std::shared_ptr<VectorTileLayerData> layerData;
- optional<FeatureIdentifier> id;
- FeatureType type = FeatureType::Unknown;
- packed_iter_type tags_iter;
- packed_iter_type geometry_iter;
-};
-
-class VectorTileLayer : public GeometryTileLayer {
-public:
- VectorTileLayer(protozero::pbf_reader, std::shared_ptr<const std::string>);
-
- std::size_t featureCount() const override { return features.size(); }
- std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const override;
- std::string getName() const override;
-
-private:
- friend class VectorTileData;
- friend class VectorTileFeature;
-
- std::string name;
- std::vector<protozero::pbf_reader> features;
- std::shared_ptr<VectorTileLayerData> data;
-};
-
-class VectorTileData : public GeometryTileData {
-public:
- VectorTileData(std::shared_ptr<const std::string> data);
-
- std::unique_ptr<GeometryTileData> clone() const override {
- return std::make_unique<VectorTileData>(*this);
- }
-
- const GeometryTileLayer* getLayer(const std::string&) const override;
-
-private:
- std::shared_ptr<const std::string> data;
- mutable bool parsed = false;
- mutable std::unordered_map<std::string, VectorTileLayer> layers;
-};
-
VectorTile::VectorTile(const OverscaledTileID& id_,
std::string sourceID_,
const TileParameters& parameters,
const Tileset& tileset)
- : GeometryTile(id_, sourceID_, parameters,
- *parameters.style.glyphAtlas,
- *parameters.style.spriteAtlas),
- loader(*this, id_, parameters, tileset) {
+ : GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) {
}
void VectorTile::setNecessity(Necessity necessity) {
@@ -104,220 +25,4 @@ void VectorTile::setData(std::shared_ptr<const std::string> data_,
GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr);
}
-Value parseValue(protozero::pbf_reader data) {
- while (data.next())
- {
- switch (data.tag()) {
- case 1: // string_value
- return data.get_string();
- case 2: // float_value
- return static_cast<double>(data.get_float());
- case 3: // double_value
- return data.get_double();
- case 4: // int_value
- return data.get_int64();
- case 5: // uint_value
- return data.get_uint64();
- case 6: // sint_value
- return data.get_sint64();
- case 7: // bool_value
- return data.get_bool();
- default:
- data.skip();
- break;
- }
- }
- return false;
-}
-
-VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr<VectorTileLayerData> layerData_)
- : layerData(std::move(layerData_)) {
- while (feature_pbf.next()) {
- switch (feature_pbf.tag()) {
- case 1: // id
- id = { feature_pbf.get_uint64() };
- break;
- case 2: // tags
- tags_iter = feature_pbf.get_packed_uint32();
- break;
- case 3: // type
- type = static_cast<FeatureType>(feature_pbf.get_enum());
- break;
- case 4: // geometry
- geometry_iter = feature_pbf.get_packed_uint32();
- break;
- default:
- feature_pbf.skip();
- break;
- }
- }
-}
-
-optional<Value> VectorTileFeature::getValue(const std::string& key) const {
- auto keyIter = layerData->keysMap.find(key);
- if (keyIter == layerData->keysMap.end()) {
- return optional<Value>();
- }
-
- auto start_itr = tags_iter.begin();
- const auto & end_itr = tags_iter.end();
- while (start_itr != end_itr) {
- uint32_t tag_key = static_cast<uint32_t>(*start_itr++);
-
- if (layerData->keysMap.size() <= tag_key) {
- throw std::runtime_error("feature referenced out of range key");
- }
-
- if (start_itr == end_itr) {
- throw std::runtime_error("uneven number of feature tag ids");
- }
-
- uint32_t tag_val = static_cast<uint32_t>(*start_itr++);;
- if (layerData->values.size() <= tag_val) {
- throw std::runtime_error("feature referenced out of range value");
- }
-
- if (tag_key == keyIter->second) {
- return layerData->values[tag_val];
- }
- }
-
- return optional<Value>();
-}
-
-std::unordered_map<std::string,Value> VectorTileFeature::getProperties() const {
- std::unordered_map<std::string,Value> properties;
- auto start_itr = tags_iter.begin();
- const auto & end_itr = tags_iter.end();
- while (start_itr != end_itr) {
- uint32_t tag_key = static_cast<uint32_t>(*start_itr++);
- if (start_itr == end_itr) {
- throw std::runtime_error("uneven number of feature tag ids");
- }
- uint32_t tag_val = static_cast<uint32_t>(*start_itr++);
- properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val);
- }
- return properties;
-}
-
-optional<FeatureIdentifier> VectorTileFeature::getID() const {
- return id;
-}
-
-GeometryCollection VectorTileFeature::getGeometries() const {
- uint8_t cmd = 1;
- uint32_t length = 0;
- int32_t x = 0;
- int32_t y = 0;
- const float scale = float(util::EXTENT) / layerData->extent;
-
- GeometryCollection lines;
-
- lines.emplace_back();
- GeometryCoordinates* line = &lines.back();
-
- auto g_itr = geometry_iter.begin();
- while (g_itr != geometry_iter.end()) {
- if (length == 0) {
- uint32_t cmd_length = static_cast<uint32_t>(*g_itr++);
- cmd = cmd_length & 0x7;
- length = cmd_length >> 3;
- }
-
- --length;
-
- if (cmd == 1 || cmd == 2) {
- x += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++));
- y += protozero::decode_zigzag32(static_cast<uint32_t>(*g_itr++));
-
- if (cmd == 1 && !line->empty()) { // moveTo
- lines.emplace_back();
- line = &lines.back();
- }
-
- line->emplace_back(::round(x * scale), ::round(y * scale));
-
- } else if (cmd == 7) { // closePolygon
- if (!line->empty()) {
- line->push_back((*line)[0]);
- }
-
- } else {
- throw std::runtime_error("unknown command");
- }
- }
-
- if (layerData->version >= 2 || type != FeatureType::Polygon) {
- return lines;
- }
-
- return fixupPolygons(lines);
-}
-
-VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_)
- : data(std::move(data_)) {
-}
-
-const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const {
- if (!parsed) {
- parsed = true;
- protozero::pbf_reader tile_pbf(*data);
- while (tile_pbf.next(3)) {
- VectorTileLayer layer(tile_pbf.get_message(), data);
- layers.emplace(layer.name, std::move(layer));
- }
- }
-
- auto it = layers.find(name);
- if (it != layers.end()) {
- return &it->second;
- }
- return nullptr;
-}
-
-VectorTileLayerData::VectorTileLayerData(std::shared_ptr<const std::string> pbfData) :
- data(std::move(pbfData))
-{}
-
-VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr<const std::string> pbfData)
- : data(std::make_shared<VectorTileLayerData>(std::move(pbfData)))
-{
- while (layer_pbf.next()) {
- switch (layer_pbf.tag()) {
- case 1: // name
- name = layer_pbf.get_string();
- break;
- case 2: // feature
- features.push_back(layer_pbf.get_message());
- break;
- case 3: // keys
- {
- auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size());
- data->keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first));
- }
- break;
- case 4: // values
- data->values.emplace_back(parseValue(layer_pbf.get_message()));
- break;
- case 5: // extent
- data->extent = layer_pbf.get_uint32();
- break;
- case 15: // version
- data->version = layer_pbf.get_uint32();
- break;
- default:
- layer_pbf.skip();
- break;
- }
- }
-}
-
-std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const {
- return std::make_unique<VectorTileFeature>(features.at(i), data);
-}
-
-std::string VectorTileLayer::getName() const {
- return name;
-}
-
} // namespace mbgl
diff --git a/src/mbgl/tile/vector_tile_data.cpp b/src/mbgl/tile/vector_tile_data.cpp
new file mode 100644
index 0000000000..2d4a01bda3
--- /dev/null
+++ b/src/mbgl/tile/vector_tile_data.cpp
@@ -0,0 +1,89 @@
+#include <mbgl/tile/vector_tile_data.hpp>
+#include <mbgl/util/constants.hpp>
+
+namespace mbgl {
+
+VectorTileFeature::VectorTileFeature(const mapbox::vector_tile::layer& layer,
+ const protozero::data_view& view)
+ : feature(view, layer) {
+}
+
+FeatureType VectorTileFeature::getType() const {
+ switch (feature.getType()) {
+ case mapbox::vector_tile::GeomType::POINT:
+ return FeatureType::Point;
+ case mapbox::vector_tile::GeomType::LINESTRING:
+ return FeatureType::LineString;
+ case mapbox::vector_tile::GeomType::POLYGON:
+ return FeatureType::Polygon;
+ default:
+ return FeatureType::Unknown;
+ }
+}
+
+optional<Value> VectorTileFeature::getValue(const std::string& key) const {
+ return feature.getValue(key);
+}
+
+std::unordered_map<std::string, Value> VectorTileFeature::getProperties() const {
+ return feature.getProperties();
+}
+
+optional<FeatureIdentifier> VectorTileFeature::getID() const {
+ return feature.getID();
+}
+
+GeometryCollection VectorTileFeature::getGeometries() const {
+ const float scale = float(util::EXTENT) / feature.getExtent();
+ auto lines = feature.getGeometries<GeometryCollection>(scale);
+ if (feature.getVersion() >= 2 || feature.getType() != mapbox::vector_tile::GeomType::POLYGON) {
+ return lines;
+ } else {
+ return fixupPolygons(lines);
+ }
+}
+
+VectorTileLayer::VectorTileLayer(std::shared_ptr<const std::string> data_,
+ const protozero::data_view& view)
+ : data(std::move(data_)), layer(view) {
+}
+
+std::size_t VectorTileLayer::featureCount() const {
+ return layer.featureCount();
+}
+
+std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const {
+ return std::make_unique<VectorTileFeature>(layer, layer.getFeature(i));
+}
+
+std::string VectorTileLayer::getName() const {
+ return layer.getName();
+}
+
+VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) : data(std::move(data_)) {
+}
+
+std::unique_ptr<GeometryTileData> VectorTileData::clone() const {
+ return std::make_unique<VectorTileData>(data);
+}
+
+std::unique_ptr<GeometryTileLayer> VectorTileData::getLayer(const std::string& name) const {
+ if (!parsed) {
+ // We're parsing this lazily so that we can construct VectorTileData objects on the main
+ // thread without incurring the overhead of parsing immediately.
+ layers = mapbox::vector_tile::buffer(*data).getLayers();
+ parsed = true;
+ }
+
+ auto it = layers.find(name);
+ if (it != layers.end()) {
+ return std::make_unique<VectorTileLayer>(data, it->second);
+ }
+ return nullptr;
+}
+
+std::vector<std::string> VectorTileData::layerNames() const {
+ return mapbox::vector_tile::buffer(*data).layerNames();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/vector_tile_data.hpp b/src/mbgl/tile/vector_tile_data.hpp
new file mode 100644
index 0000000000..48beaf9d07
--- /dev/null
+++ b/src/mbgl/tile/vector_tile_data.hpp
@@ -0,0 +1,54 @@
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+#include <mapbox/vector_tile.hpp>
+#include <protozero/pbf_reader.hpp>
+
+#include <unordered_map>
+#include <functional>
+#include <utility>
+
+namespace mbgl {
+
+class VectorTileFeature : public GeometryTileFeature {
+public:
+ VectorTileFeature(const mapbox::vector_tile::layer&, const protozero::data_view&);
+
+ FeatureType getType() const override;
+ optional<Value> getValue(const std::string& key) const override;
+ std::unordered_map<std::string, Value> getProperties() const override;
+ optional<FeatureIdentifier> getID() const override;
+ GeometryCollection getGeometries() const override;
+
+private:
+ mapbox::vector_tile::feature feature;
+};
+
+class VectorTileLayer : public GeometryTileLayer {
+public:
+ VectorTileLayer(std::shared_ptr<const std::string> data, const protozero::data_view&);
+
+ std::size_t featureCount() const override;
+ std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override;
+ std::string getName() const override;
+
+private:
+ std::shared_ptr<const std::string> data;
+ mapbox::vector_tile::layer layer;
+};
+
+class VectorTileData : public GeometryTileData {
+public:
+ VectorTileData(std::shared_ptr<const std::string> data);
+
+ std::unique_ptr<GeometryTileData> clone() const override;
+ std::unique_ptr<GeometryTileLayer> getLayer(const std::string& name) const override;
+
+ std::vector<std::string> layerNames() const;
+
+private:
+ std::shared_ptr<const std::string> data;
+ mutable bool parsed = false;
+ mutable std::map<std::string, const protozero::data_view> layers;
+};
+
+} // namespace mbgl
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/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/i18n.cpp b/src/mbgl/util/i18n.cpp
index 6cfdc697e3..16f1d669f3 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)
@@ -311,7 +318,7 @@ const std::map<char16_t, char16_t> verticalPunctuation = {
{ u'{', u'︷' }, { u'|', u'―' }, { u'}', u'︸' }, { u'⦅', u'︵' }, { u'⦆', u'︶' },
{ u'。', u'︒' }, { u'「', u'﹁' }, { u'」', u'﹂' },
};
-}
+} // namespace
namespace mbgl {
namespace util {
@@ -375,11 +382,13 @@ 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));
}
@@ -393,7 +402,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 +466,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 +489,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;
diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp
index c580357298..e6ce245c0e 100644
--- a/src/mbgl/util/intersection_tests.cpp
+++ b/src/mbgl/util/intersection_tests.cpp
@@ -19,7 +19,7 @@ bool polygonContainsPoint(const GeometryCoordinates& ring, const GeometryCoordin
// Code from http://stackoverflow.com/a/1501725/331379.
float distToSegmentSquared(const GeometryCoordinate& p, const GeometryCoordinate& v, const GeometryCoordinate& w) {
if (v == w) return util::distSqr<float>(p, v);
- const float l2 = util::distSqr<float>(v, w);
+ const auto l2 = util::distSqr<float>(v, w);
const float t = float((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) return util::distSqr<float>(p, v);
if (t > 1) return util::distSqr<float>(p, w);
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/logging.cpp b/src/mbgl/util/logging.cpp
index 939f1def64..0552eb36cb 100644
--- a/src/mbgl/util/logging.cpp
+++ b/src/mbgl/util/logging.cpp
@@ -1,6 +1,6 @@
#include <mbgl/util/logging.hpp>
#include <mbgl/util/enum.hpp>
-#include <mbgl/util/thread.hpp>
+#include <mbgl/util/platform.hpp>
#include <cstdio>
#include <cstdarg>
diff --git a/src/mbgl/util/longest_common_subsequence.hpp b/src/mbgl/util/longest_common_subsequence.hpp
new file mode 100644
index 0000000000..522b5a86b1
--- /dev/null
+++ b/src/mbgl/util/longest_common_subsequence.hpp
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <vector>
+
+namespace mbgl {
+
+/*
+ Computes the longest common subsequence (LCS) of sequences A and B, represented
+ by pairs of random access iterators. The result is output to the provided output
+ iterator. Equality of elements is determined by the provided comparator, defaulting
+ to ==.
+
+ The algorithm used is the O(ND) time and space algorithm from:
+
+ Myers, Eugene W. An O(ND) Difference Algorithm and Its Variations. Algorithmica
+ (1986) 1: 251. http://xmailserver.org/diff2.pdf
+
+ For understanding this algorithm, http://simplygenius.net/Article/DiffTutorial1 is
+ also helpful.
+
+ TODO: implement the O(N) space refinement presented in the paper.
+*/
+template <class InIt, class OutIt, class Equal>
+OutIt longest_common_subsequence(InIt a, InIt endA,
+ InIt b, InIt endB,
+ OutIt outIt,
+ Equal eq) {
+ const std::ptrdiff_t N = endA - a;
+ const std::ptrdiff_t M = endB - b;
+ const std::ptrdiff_t D = N + M;
+
+ if (D == 0) {
+ return outIt;
+ }
+
+ std::vector<std::vector<std::ptrdiff_t>> vs;
+
+ // Self-executing lambda to allow `return` to break from inner loop, and avoid shadowing `v`.
+ [&] () {
+ std::vector<std::ptrdiff_t> v;
+ v.resize(2 * D + 1);
+ v[1] = 0;
+
+ // Core of the algorithm: greedily find farthest-reaching D-paths for increasing
+ // values of D. Store the farthest-reaching endpoints found in each iteration for
+ // later reconstructing the LCS.
+ for (std::ptrdiff_t d = 0; d <= D; ++d) {
+ for (std::ptrdiff_t k = -d; k <= d; k += 2) {
+ std::ptrdiff_t x = (k == -d || (k != d && v.at(k - 1 + D) < v.at(k + 1 + D)))
+ ? v.at(k + 1 + D) // moving down
+ : v.at(k - 1 + D) + 1; // moving right
+
+ std::ptrdiff_t y = x - k;
+
+ while (x < N && y < M && eq(a[x], b[y])) {
+ x++;
+ y++;
+ }
+
+ v[k + D] = x;
+
+ if (x >= N && y >= M) {
+ vs.push_back(v);
+ return;
+ }
+ }
+
+ vs.push_back(v);
+ }
+ }();
+
+ std::ptrdiff_t x = N;
+ std::ptrdiff_t y = M;
+
+ using E = typename std::iterator_traits<InIt>::value_type;
+ std::vector<E> lcsReverse;
+
+ // Reconstruct the LCS using the farthest-reaching endpoints stored above.
+ for (std::ptrdiff_t d = vs.size() - 1; x > 0 || y > 0; --d) {
+ const std::vector<std::ptrdiff_t>& v = vs.at(d);
+ const std::ptrdiff_t k = x - y;
+ const bool down = (k == -d || (k != d && v.at(k - 1 + D) < v.at(k + 1 + D)));
+ const std::ptrdiff_t kPrev = down ? k + 1 : k - 1;
+
+ x = v.at(kPrev + D);
+ y = x - kPrev;
+
+ for (std::ptrdiff_t c = v[k + D]; c != (down ? x : x + 1); --c) {
+ lcsReverse.push_back(a[c - 1]);
+ }
+ }
+
+ return std::copy(lcsReverse.rbegin(), lcsReverse.rend(), outIt);
+}
+
+template < typename InIt, typename OutIt >
+OutIt longest_common_subsequence(InIt a, InIt endA,
+ InIt b, InIt endB,
+ OutIt outIt) {
+ return longest_common_subsequence(a, endA, b, endB, outIt, std::equal_to<>());
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/util/mat2.hpp b/src/mbgl/util/mat2.hpp
index 6a25ef0f1e..c463202daa 100644
--- a/src/mbgl/util/mat2.hpp
+++ b/src/mbgl/util/mat2.hpp
@@ -26,7 +26,7 @@
namespace mbgl {
-typedef std::array<double, 4> mat2;
+using mat2 = std::array<double, 4>;
namespace matrix {
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 fe24774b7c..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,8 @@ public:
context.bindFramebuffer = framebuffer->framebuffer;
}
- context.activeTexture = 0;
+ context.activeTextureUnit = 0;
+ context.scissorTest = false;
context.viewport = { 0, 0, size };
}
@@ -52,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 ae96286340..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);
+ 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/premultiply.cpp b/src/mbgl/util/premultiply.cpp
index 219273d7cc..d9fb2480de 100644
--- a/src/mbgl/util/premultiply.cpp
+++ b/src/mbgl/util/premultiply.cpp
@@ -9,6 +9,7 @@ PremultipliedImage premultiply(UnassociatedImage&& src) {
PremultipliedImage dst;
dst.size = src.size;
+ src.size = { 0, 0 };
dst.data = std::move(src.data);
uint8_t* data = dst.data.get();
@@ -29,6 +30,7 @@ UnassociatedImage unpremultiply(PremultipliedImage&& src) {
UnassociatedImage dst;
dst.size = src.size;
+ src.size = { 0, 0 };
dst.data = std::move(src.data);
uint8_t* data = dst.data.get();
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
deleted file mode 100644
index 184c6a8a12..0000000000
--- a/src/mbgl/util/thread.hpp
+++ /dev/null
@@ -1,173 +0,0 @@
-#pragma once
-
-#include <cassert>
-#include <future>
-#include <thread>
-#include <atomic>
-#include <utility>
-#include <functional>
-
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/thread_context.hpp>
-#include <mbgl/util/platform.hpp>
-#include <mbgl/util/util.hpp>
-
-namespace mbgl {
-namespace util {
-
-// Manages a thread with Object.
-
-// Upon creation of this object, it launches a thread, creates an object of type Object in that
-// thread, and then calls .start(); on that object. When the Thread<> object is destructed, the
-// Object's .stop() function is called, and the destructor waits for thread termination. The
-// Thread<> constructor blocks until the thread and the Object are fully created, so after the
-// object creation, it's safe to obtain the Object stored in this thread.
-
-template <class Object>
-class Thread {
-public:
- template <class... Args>
- Thread(const ThreadContext&, Args&&... args);
- ~Thread();
-
- // Invoke object->fn(args...) asynchronously.
- template <typename Fn, class... Args>
- void invoke(Fn fn, Args&&... args) {
- loop->invoke(bind(fn), std::forward<Args>(args)...);
- }
-
- // Invoke object->fn(args...) asynchronously. The final argument to fn must be a callback.
- // The provided callback is wrapped such that it is invoked, in the current thread (which
- // must have a RunLoop), once for each time the invocation of fn invokes the wrapper, each
- // time forwarding the passed arguments, until such time as the AsyncRequest is cancelled.
- template <typename Fn, class... Args>
- std::unique_ptr<AsyncRequest>
- invokeWithCallback(Fn fn, Args&&... args) {
- return loop->invokeWithCallback(bind(fn), std::forward<Args>(args)...);
- }
-
- // Invoke object->fn(args...) asynchronously, but wait for the result.
- template <typename Fn, class... Args>
- auto invokeSync(Fn fn, Args&&... args) {
- assert(!paused);
-
- using R = std::result_of_t<Fn(Object, Args&&...)>;
- std::packaged_task<R ()> task(std::bind(fn, object, args...));
- std::future<R> future = task.get_future();
- loop->invoke(std::move(task));
- return future.get();
- }
-
- void pause() {
- MBGL_VERIFY_THREAD(tid);
-
- assert(!paused);
-
- paused = std::make_unique<std::promise<void>>();
- resumed = std::make_unique<std::promise<void>>();
-
- auto pausing = paused->get_future();
-
- loop->invoke([this] {
- auto resuming = resumed->get_future();
- paused->set_value();
- resuming.get();
- });
-
- pausing.get();
- }
-
- void resume() {
- MBGL_VERIFY_THREAD(tid);
-
- assert(paused);
-
- resumed->set_value();
-
- resumed.reset();
- paused.reset();
- }
-
-private:
- MBGL_STORE_THREAD(tid);
-
- Thread(const Thread&) = delete;
- Thread(Thread&&) = delete;
- Thread& operator=(const Thread&) = delete;
- Thread& operator=(Thread&&) = delete;
-
- template <typename Fn>
- auto bind(Fn fn) {
- return [fn, this] (auto &&... args) {
- return (object->*fn)(std::forward<decltype(args)>(args)...);
- };
- }
-
- template <typename P, std::size_t... I>
- void run(P&& params, std::index_sequence<I...>);
-
- std::promise<void> running;
- std::promise<void> joinable;
-
- std::unique_ptr<std::promise<void>> paused;
- std::unique_ptr<std::promise<void>> resumed;
-
- std::thread thread;
-
- Object* object = nullptr;
- RunLoop* loop = nullptr;
-};
-
-template <class Object>
-template <class... Args>
-Thread<Object>::Thread(const ThreadContext& context, Args&&... args) {
- // Note: We're using std::tuple<> to store the arguments because GCC 4.9 has a bug
- // when expanding parameters packs captured in lambdas.
- std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...);
-
- thread = std::thread([&] {
- platform::setCurrentThreadName(context.name);
-
- if (context.priority == ThreadPriority::Low) {
- platform::makeThreadLowPriority();
- }
-
- run(std::move(params), std::index_sequence_for<Args...>{});
- });
-
- running.get_future().get();
-}
-
-template <class Object>
-template <typename P, std::size_t... I>
-void Thread<Object>::run(P&& params, std::index_sequence<I...>) {
- RunLoop loop_(RunLoop::Type::New);
- loop = &loop_;
-
- Object object_(std::get<I>(std::forward<P>(params))...);
- object = &object_;
-
- running.set_value();
- loop_.run();
-
- loop = nullptr;
- object = nullptr;
-
- joinable.get_future().get();
-}
-
-template <class Object>
-Thread<Object>::~Thread() {
- MBGL_VERIFY_THREAD(tid);
-
- if (paused) {
- resume();
- }
-
- loop->stop();
- joinable.set_value();
- thread.join();
-}
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/thread_context.cpp b/src/mbgl/util/thread_context.cpp
deleted file mode 100644
index fe64c2a686..0000000000
--- a/src/mbgl/util/thread_context.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <mbgl/util/thread_context.hpp>
-#include <utility>
-
-namespace mbgl {
-namespace util {
-
-ThreadContext::ThreadContext(std::string name_, ThreadPriority priority_)
- : name(std::move(name_)),
- priority(priority_) {
-}
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/thread_context.hpp b/src/mbgl/util/thread_context.hpp
deleted file mode 100644
index a51dede404..0000000000
--- a/src/mbgl/util/thread_context.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include <string>
-
-namespace mbgl {
-namespace util {
-
-enum class ThreadPriority : bool {
- Regular,
- Low,
-};
-
-struct ThreadContext {
-public:
- ThreadContext(std::string name, ThreadPriority priority = ThreadPriority::Regular);
-
- std::string name;
- ThreadPriority priority;
-};
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/thread_local.hpp b/src/mbgl/util/thread_local.hpp
index 9fddbd5bbc..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() {
- T* 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..a5a1b1d70c 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -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 = std::fmax(std::min(x1, x2), 0);
+ auto maxX = std::max(x1, x2);
+ auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2);
+ auto maxY = (std::pow(2, zoom) - 1) - std::fmax(std::min(y1, y2), 0);
+
+ return (maxX - minX + 1) * (maxY - minY + 1);
+}
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index 2d32d8bf41..3c7a4ee44a 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -18,5 +18,8 @@ int32_t coveringZoomLevel(double z, 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/work_queue.cpp b/src/mbgl/util/work_queue.cpp
deleted file mode 100644
index d0033e3ca2..0000000000
--- a/src/mbgl/util/work_queue.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#include <mbgl/util/work_queue.hpp>
-#include <mbgl/util/run_loop.hpp>
-
-#include <cassert>
-
-namespace mbgl {
-namespace util {
-
-WorkQueue::WorkQueue() : runLoop(RunLoop::Get()) {
-}
-
-WorkQueue::~WorkQueue() {
- assert(runLoop == RunLoop::Get());
-
- // Cancel all pending AsyncRequests.
- while (!queue.empty()) {
- queue.pop();
- }
-}
-
-void WorkQueue::push(std::function<void()>&& fn) {
- std::lock_guard<std::mutex> lock(queueMutex);
-
- auto workRequest = runLoop->invokeCancellable(std::bind(&WorkQueue::pop, this, std::move(fn)));
- queue.push(std::move(workRequest));
-}
-
-void WorkQueue::pop(const std::function<void()>& fn) {
- assert(runLoop == RunLoop::Get());
-
- fn();
-
- std::lock_guard<std::mutex> lock(queueMutex);
- queue.pop();
-}
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/work_queue.hpp b/src/mbgl/util/work_queue.hpp
deleted file mode 100644
index 3f6328fb57..0000000000
--- a/src/mbgl/util/work_queue.hpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/async_request.hpp>
-
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <queue>
-
-namespace mbgl {
-namespace util {
-
-class RunLoop;
-
-// The WorkQueue will manage a queue of closures
-// and it will make sure they get executed on the
-// thread that created the WorkQueue. All pending
-// works are canceled when the queue gets destructed.
-class WorkQueue : private util::noncopyable {
-public:
- WorkQueue();
- ~WorkQueue();
-
- // Push a closure to the queue. It is advised to
- // only push tasks calling functions on the object
- // that owns the queue to avoid use after free errors.
- void push(std::function<void()>&&);
-
-private:
- void pop(const std::function<void()>&);
-
- std::queue<std::unique_ptr<AsyncRequest>> queue;
- std::mutex queueMutex;
-
- RunLoop* runLoop;
-};
-
-} // namespace util
-} // namespace mbgl
diff --git a/test/.clang-tidy b/test/.clang-tidy
new file mode 100644
index 0000000000..492d4affbd
--- /dev/null
+++ b/test/.clang-tidy
@@ -0,0 +1,2 @@
+Checks: 'modernize-*,-modernize-use-equals-delete,-modernize-use-equals-default,misc-static-assert,llvm-namespace-comment,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-core.uninitialized.UndefReturn,-clang-analyzer-core.StackAddressEscape,-clang-analyzer-core.CallAndMessage,-clang-diagnostic-unused-command-line-argument,-clang-analyzer-core.uninitialized.*,-clang-analyzer-core.NullDereference,-clang-analyzer-core.NonNullParamChecker'
+HeaderFilterRegex: '\/mbgl\/'
diff --git a/test/actor/actor.test.cpp b/test/actor/actor.test.cpp
index 03f41a6e64..967dc152d9 100644
--- a/test/actor/actor.test.cpp
+++ b/test/actor/actor.test.cpp
@@ -6,6 +6,7 @@
#include <chrono>
#include <functional>
#include <future>
+#include <memory>
using namespace mbgl;
using namespace std::chrono_literals;
@@ -26,7 +27,7 @@ TEST(Actor, Construction) {
EXPECT_TRUE(constructed);
}
-TEST(Actor, DestructionClosesMailbox) {
+TEST(Actor, DestructionBlocksOnReceive) {
// Destruction blocks until the actor is not receiving.
struct Test {
@@ -67,6 +68,149 @@ TEST(Actor, DestructionClosesMailbox) {
exitingPromise.set_value();
}
+TEST(Actor, DestructionBlocksOnSend) {
+ // Destruction blocks until the actor is not being sent a message.
+
+ struct TestScheduler : public Scheduler {
+ std::promise<void> promise;
+ std::future<void> future;
+ std::atomic<bool> waited;
+
+ TestScheduler(std::promise<void> promise_, std::future<void> future_)
+ : promise(std::move(promise_)),
+ future(std::move(future_)),
+ waited(false) {
+ }
+
+ ~TestScheduler() {
+ EXPECT_TRUE(waited.load());
+ }
+
+ void schedule(std::weak_ptr<Mailbox>) final {
+ promise.set_value();
+ future.wait();
+ std::this_thread::sleep_for(1ms);
+ waited = true;
+ }
+ };
+
+ struct Test {
+ Test(ActorRef<Test>) {}
+ void message() {}
+ };
+
+ std::promise<void> enteredPromise;
+ std::future<void> enteredFuture = enteredPromise.get_future();
+
+ std::promise<void> exitingPromise;
+ std::future<void> exitingFuture = exitingPromise.get_future();
+
+ auto scheduler = std::make_unique<TestScheduler>(std::move(enteredPromise), std::move(exitingFuture));
+ auto actor = std::make_unique<Actor<Test>>(*scheduler);
+
+ std::thread thread {
+ [] (ActorRef<Test> ref) {
+ ref.invoke(&Test::message);
+ },
+ actor->self()
+ };
+
+ enteredFuture.wait();
+ exitingPromise.set_value();
+
+ actor.reset();
+ scheduler.reset();
+
+ thread.join();
+}
+
+TEST(Actor, DestructionAllowedInReceiveOnSameThread) {
+ // Destruction doesn't block if occurring on the same
+ // thread as receive(). This prevents deadlocks and
+ // allows for self-closing actors
+
+ struct Test {
+
+ Test(ActorRef<Test>){};
+
+ void callMeBack(std::function<void ()> callback) {
+ callback();
+ }
+ };
+
+ ThreadPool pool { 1 };
+
+ std::promise<void> callbackFiredPromise;
+
+ auto test = std::make_unique<Actor<Test>>(pool);
+
+ // Callback (triggered while mutex is locked in Mailbox::receive())
+ test->invoke(&Test::callMeBack, [&]() {
+ // Destroy the Actor/Mailbox in the same thread
+ test.reset();
+ callbackFiredPromise.set_value();
+ });
+
+ auto status = callbackFiredPromise.get_future().wait_for(std::chrono::seconds(1));
+ ASSERT_EQ(std::future_status::ready, status);
+}
+
+TEST(Actor, SelfDestructionDoesntCrashWaitingReceivingThreads) {
+ // Ensures destruction doesn't cause waiting threads to
+ // crash when a actor closes it's own mailbox from a
+ // callback
+
+ struct Test {
+
+ Test(ActorRef<Test>){};
+
+ void callMeBack(std::function<void ()> callback) {
+ callback();
+ }
+ };
+
+
+ ThreadPool pool { 2 };
+
+ std::promise<void> actorClosedPromise;
+
+ auto closingActor = std::make_unique<Actor<Test>>(pool);
+ auto waitingActor = std::make_unique<Actor<Test>>(pool);
+
+ std::atomic<bool> waitingMessageProcessed {false};
+
+ // Callback (triggered while mutex is locked in Mailbox::receive())
+ closingActor->invoke(&Test::callMeBack, [&]() {
+
+ // Queue up another message from another thread
+ std::promise<void> messageQueuedPromise;
+ waitingActor->invoke(&Test::callMeBack, [&]() {
+ // This will be waiting on the mutex in
+ // Mailbox::receive(), holding a lock
+ // on the weak_ptr so the mailbox is not
+ // destroyed
+ closingActor->invoke(&Test::callMeBack, [&]() {
+ waitingMessageProcessed.store(true);
+ });
+ messageQueuedPromise.set_value();
+ });
+
+ // Wait for the message to be queued
+ ASSERT_EQ(
+ messageQueuedPromise.get_future().wait_for(std::chrono::seconds(1)),
+ std::future_status::ready
+ );
+
+ // Destroy the Actor/Mailbox in the same thread
+ closingActor.reset();
+ actorClosedPromise.set_value();
+ });
+
+ auto status = actorClosedPromise.get_future().wait_for(std::chrono::seconds(1));
+ ASSERT_EQ(std::future_status::ready, status);
+ ASSERT_FALSE(waitingMessageProcessed.load());
+}
+
TEST(Actor, OrderedMailbox) {
// Messages are processed in order.
@@ -137,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..1ebdccb99e 100644
--- a/test/algorithm/generate_clip_ids.test.cpp
+++ b/test/algorithm/generate_clip_ids.test.cpp
@@ -5,173 +5,150 @@
using namespace mbgl;
struct Renderable {
+ UnwrappedTileID id;
ClipID clip;
- bool used = true;
+ bool used;
+
+ Renderable(UnwrappedTileID id_,
+ ClipID clip_,
+ bool used_ = true)
+ : id(std::move(id_)),
+ clip(std::move(clip_)),
+ used(used_) {}
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 +157,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 +213,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 +265,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 +329,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/update_renderables.test.cpp b/test/algorithm/update_renderables.test.cpp
index af90d262de..2d37992579 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,9 +38,9 @@ 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 {
@@ -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 == Resource::Necessity::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) {
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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Optional }, //
+
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // child tile found
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // use overzoomed tile!
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 4, 0, { 4, 0, 0 } } }, //
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 3, 0, { 3, 0, 0 } } }, //
+ RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, Resource::Necessity::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 } }, Resource::Necessity::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 } }, Resource::Necessity::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 97ccaae684..9e622f780a 100644
--- a/test/api/annotations.test.cpp
+++ b/test/api/annotations.test.cpp
@@ -3,37 +3,40 @@
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/annotation/annotation.hpp>
+#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;
namespace {
+PremultipliedImage namedImage(const std::string& name) {
+ return decodeImage(util::read_file("test/fixtures/sprites/" + name + ".png"));
+}
+
std::unique_ptr<style::Image> namedMarker(const std::string& name) {
- PremultipliedImage image = decodeImage(util::read_file("test/fixtures/sprites/" + name));
- return std::make_unique<style::Image>(std::move(image), 1.0);
+ return std::make_unique<style::Image>(name, namedImage(name), 1.0);
}
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::Still };
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);
}
};
@@ -42,12 +45,12 @@ public:
TEST(Annotations, SymbolAnnotation) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
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);
@@ -64,7 +67,7 @@ TEST(Annotations, LineAnnotation) {
annotation.color = Color::red();
annotation.width = { 5 };
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.map.addAnnotation(annotation);
test.checkRendering("line_annotation");
@@ -79,7 +82,7 @@ TEST(Annotations, FillAnnotation) {
FillAnnotation annotation { polygon };
annotation.color = Color::red();
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.map.addAnnotation(annotation);
test.checkRendering("fill_annotation");
@@ -92,7 +95,7 @@ TEST(Annotations, AntimeridianAnnotationSmall) {
double antimeridian = 180;
test.map.setLatLngZoom(mbgl::LatLng(0, antimeridian), 0);
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
LineString<double> line = {{ { antimeridian, 20 }, { antimeridian, -20 } }};
LineAnnotation lineAnnotation { line };
@@ -113,7 +116,7 @@ TEST(Annotations, AntimeridianAnnotationLarge) {
double antimeridian = 180;
test.map.setLatLngZoom(mbgl::LatLng(0, antimeridian), 0);
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
LineString<double> line = {{ { antimeridian, 20 }, { antimeridian, -20 } }};
LineAnnotation lineAnnotation { line };
@@ -138,30 +141,20 @@ TEST(Annotations, OverlappingFillAnnotation) {
FillAnnotation overlaidAnnotation { polygon };
overlaidAnnotation.color = Color::red();
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.map.addAnnotation(underlaidAnnotation);
test.map.addAnnotation(overlaidAnnotation);
test.checkRendering("overlapping_fill_annotation");
}
-TEST(Annotations, StyleSourcedShapeAnnotation) {
- AnnotationTest test;
-
- Polygon<double> polygon = { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} };
-
- test.map.setStyleJSON(util::read_file("test/fixtures/api/annotation.json"));
- test.map.addAnnotation(StyleSourcedAnnotation { polygon, "annotation" });
- test.checkRendering("style_sourced_shape_annotation");
-}
-
TEST(Annotations, AddMultiple) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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");
@@ -170,8 +163,8 @@ TEST(Annotations, AddMultiple) {
TEST(Annotations, NonImmediateAdd) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test::render(test.map, test.view);
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.frontend.render(test.map);
Polygon<double> polygon = { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} };
FillAnnotation annotation { polygon };
@@ -184,12 +177,12 @@ TEST(Annotations, NonImmediateAdd) {
TEST(Annotations, UpdateSymbolAnnotationGeometry) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
- test.map.addAnnotationImage("flipped_marker", namedMarker("flipped_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
+ 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");
@@ -198,12 +191,12 @@ TEST(Annotations, UpdateSymbolAnnotationGeometry) {
TEST(Annotations, UpdateSymbolAnnotationIcon) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
- test.map.addAnnotationImage("flipped_marker", namedMarker("flipped_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
+ 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");
@@ -216,10 +209,10 @@ TEST(Annotations, UpdateLineAnnotationGeometry) {
annotation.color = Color::red();
annotation.width = { 5 };
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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);
@@ -233,10 +226,10 @@ TEST(Annotations, UpdateLineAnnotationStyle) {
annotation.color = Color::red();
annotation.width = { 5 };
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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 };
@@ -250,10 +243,10 @@ TEST(Annotations, UpdateFillAnnotationGeometry) {
FillAnnotation annotation { Polygon<double> { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} } };
annotation.color = Color::red();
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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);
@@ -267,10 +260,10 @@ TEST(Annotations, UpdateFillAnnotationStyle) {
FillAnnotation annotation { polygon };
annotation.color = Color::red();
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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);
@@ -280,11 +273,11 @@ TEST(Annotations, UpdateFillAnnotationStyle) {
TEST(Annotations, RemovePoint) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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");
@@ -298,10 +291,10 @@ TEST(Annotations, RemoveShape) {
annotation.color = Color::red();
annotation.width = { 5 };
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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");
@@ -311,40 +304,53 @@ TEST(Annotations, ImmediateRemoveShape) {
AnnotationTest test;
test.map.removeAnnotation(test.map.addAnnotation(LineAnnotation { LineString<double>() }));
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.checkRendering("switch_style");
}
+TEST(Annotations, ReaddImage) {
+ AnnotationTest test;
+
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
+ test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
+
+ test.frontend.render(test.map);
+
+ test.map.addAnnotationImage(std::make_unique<style::Image>("default_marker", namedImage("flipped_marker"), 1.0));
+ test.checkRendering("readd_image");
+}
+
TEST(Annotations, QueryRenderedFeatures) {
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
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));
@@ -353,11 +359,11 @@ 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.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ 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) {
@@ -369,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.
@@ -385,11 +391,11 @@ 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.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
test.map.setLatLngZoom({ 5, 5 }, 3);
std::vector<mbgl::AnnotationID> ids;
@@ -401,9 +407,9 @@ TEST(Annotations, VisibleFeatures) {
// 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);
@@ -412,13 +418,19 @@ 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());
}
+TEST(Annotations, TopOffsetPixels) {
+ AnnotationTest test;
+
+ test.map.addAnnotationImage(namedMarker("default_marker"));
+ EXPECT_EQ(test.map.getTopOffsetPixelsForAnnotationImage("default_marker"), -28);
+}
TEST(Annotations, DebugEmpty) {
// This test should render nothing, not even the tile borders. Tile borders are only rendered
@@ -426,7 +438,7 @@ TEST(Annotations, DebugEmpty) {
// should not render them.
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.map.setDebug(MapDebugOptions::TileBorders);
test.map.setZoom(1);
@@ -439,10 +451,10 @@ TEST(Annotations, DebugSparse) {
// tiles because they're all empty.
AnnotationTest test;
- test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.map.setDebug(MapDebugOptions::TileBorders);
test.map.setZoom(1);
- test.map.addAnnotationImage("default_marker", namedMarker("default_marker.png"));
+ test.map.addAnnotationImage(namedMarker("default_marker"));
test.map.addAnnotation(SymbolAnnotation { Point<double>(10, 10), "default_marker" });
test.checkRendering("debug_sparse");
diff --git a/test/api/api_misuse.test.cpp b/test/api/api_misuse.test.cpp
index af703fddfb..690c1548e5 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::Still);
+ map->renderStill(nullptr);
// Force Map thread to join.
map.reset();
@@ -44,31 +42,3 @@ TEST(API, RenderWithoutCallback) {
EXPECT_EQ(log->count(logMessage), 1u);
}
-
-TEST(API, RenderWithoutStyle) {
- util::RunLoop loop;
-
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext(), { 128, 512 } };
- StubFileSource fileSource;
- ThreadPool threadPool(4);
-
- Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
-
- std::exception_ptr error;
- map.renderStill(view, [&](std::exception_ptr error_) {
- error = error_;
- loop.stop();
- });
-
- loop.run();
-
- try {
- std::rethrow_exception(error);
- } catch (const util::MisuseException& ex) {
- EXPECT_EQ(std::string(ex.what()), "Map doesn't have a style");
- } catch (const std::exception&) {
- EXPECT_TRUE(false) << "Unhandled exception.";
- }
-}
diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp
index 5a30220cd7..1c514aeca2 100644
--- a/test/api/custom_layer.test.cpp
+++ b/test/api/custom_layer.test.cpp
@@ -2,11 +2,10 @@
#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>
#include <mbgl/util/io.hpp>
@@ -86,16 +85,15 @@ 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);
- map.setStyleJSON(util::read_file("test/fixtures/api/water.json"));
+ float pixelRatio { 1 };
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
+ threadPool, MapMode::Still);
+ map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
map.setLatLngZoom({ 37.8, -122.5 }, 10);
- map.addLayer(std::make_unique<CustomLayer>(
+ map.getStyle().addLayer(std::make_unique<CustomLayer>(
"custom",
[] (void* context) {
reinterpret_cast<TestLayer*>(context)->initialize();
@@ -110,7 +108,7 @@ TEST(CustomLayer, Basic) {
auto layer = std::make_unique<FillLayer>("landcover", "mapbox");
layer->setSourceLayer("landcover");
layer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 });
- map.addLayer(std::move(layer));
+ 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 c509753d2d..3f3825eb72 100644
--- a/test/api/query.test.cpp
+++ b/test/api/query.test.cpp
@@ -1,15 +1,15 @@
#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>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
+#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;
@@ -19,20 +19,20 @@ namespace {
class QueryTest {
public:
QueryTest() {
- map.setStyleJSON(util::read_file("test/fixtures/api/query_style.json"));
- map.addImage("test-icon", std::make_unique<style::Image>(
+ map.getStyle().loadJSON(util::read_file("test/fixtures/api/query_style.json"));
+ 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::Still };
};
} // end namespace
@@ -40,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);
}
@@ -52,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);
}
@@ -71,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);
}
@@ -94,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);
}
@@ -110,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..8cd622fe79
--- /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::Still);
+
+ 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 6e99501708..0000000000
--- a/test/api/render_missing.test.cpp
+++ /dev/null
@@ -1,64 +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 <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.setStyleJSON(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 0b9726d3fa..0000000000
--- a/test/api/repeated_render.test.cpp
+++ /dev/null
@@ -1,75 +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 <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.setStyleJSON(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.setStyleJSON(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..1b1159bf52
--- /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::Still);
+
+ 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/readd_image/expected.png b/test/fixtures/annotations/readd_image/expected.png
new file mode 100644
index 0000000000..3c4847f3a7
--- /dev/null
+++ b/test/fixtures/annotations/readd_image/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/image_manager/basic/expected.png b/test/fixtures/image_manager/basic/expected.png
new file mode 100644
index 0000000000..8c615234dc
--- /dev/null
+++ b/test/fixtures/image_manager/basic/expected.png
Binary files differ
diff --git a/test/fixtures/image_manager/updates_after/expected.png b/test/fixtures/image_manager/updates_after/expected.png
new file mode 100644
index 0000000000..db588c739b
--- /dev/null
+++ b/test/fixtures/image_manager/updates_after/expected.png
Binary files differ
diff --git a/test/fixtures/image_manager/updates_before/expected.png b/test/fixtures/image_manager/updates_before/expected.png
new file mode 100644
index 0000000000..1466a92fe7
--- /dev/null
+++ b/test/fixtures/image_manager/updates_before/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/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_download/radar.gif b/test/fixtures/offline_download/radar.gif
new file mode 100644
index 0000000000..7398a060c0
--- /dev/null
+++ b/test/fixtures/offline_download/radar.gif
Binary files differ
diff --git a/test/fixtures/offline_download/style.json b/test/fixtures/offline_download/style.json
index 978df3aae3..618afd45f0 100644
--- a/test/fixtures/offline_download/style.json
+++ b/test/fixtures/offline_download/style.json
@@ -5,6 +5,16 @@
"mapbox": {
"type": "vector",
"url": "http://127.0.0.1:3000/streets.json"
+ },
+ "radar": {
+ "type": "image",
+ "url":"http://127.0.0.1:3000/radar.gif",
+ "coordinates": [
+ [-180, -85.0511],
+ [180, -85.0511],
+ [180, 85.0511],
+ [-180, 85.0511]
+ ]
}
},
"glyphs": "http://127.0.0.1:3000/{fontstack}/{range}.pbf",
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/sprite_atlas/basic/expected.png b/test/fixtures/sprite_atlas/basic/expected.png
deleted file mode 100644
index cd13d16df6..0000000000
--- a/test/fixtures/sprite_atlas/basic/expected.png
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/sprite_atlas/size/expected.png b/test/fixtures/sprite_atlas/size/expected.png
deleted file mode 100644
index d9ae7dab47..0000000000
--- a/test/fixtures/sprite_atlas/size/expected.png
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/sprite_atlas/updates_after/expected.png b/test/fixtures/sprite_atlas/updates_after/expected.png
deleted file mode 100644
index 3c850c0a25..0000000000
--- a/test/fixtures/sprite_atlas/updates_after/expected.png
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/sprite_atlas/updates_before/expected.png b/test/fixtures/sprite_atlas/updates_before/expected.png
deleted file mode 100644
index effcd38f1e..0000000000
--- a/test/fixtures/sprite_atlas/updates_before/expected.png
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/style_parser/center-not-latlong.info.json b/test/fixtures/style_parser/center-not-latlong.info.json
new file mode 100644
index 0000000000..c79de0a525
--- /dev/null
+++ b/test/fixtures/style_parser/center-not-latlong.info.json
@@ -0,0 +1,7 @@
+{
+ "default": {
+ "log": [
+ [1, "WARNING", "ParseStyle", "center coordinate must be a longitude, latitude pair"]
+ ]
+ }
+} \ No newline at end of file
diff --git a/test/fixtures/style_parser/center-not-latlong.style.json b/test/fixtures/style_parser/center-not-latlong.style.json
new file mode 100644
index 0000000000..eb847868f5
--- /dev/null
+++ b/test/fixtures/style_parser/center-not-latlong.style.json
@@ -0,0 +1,4 @@
+{
+ "version": 8,
+ "center" : [ 75.123, 181.123]
+} \ No newline at end of file
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/style_parser/image-coordinates.info.json b/test/fixtures/style_parser/image-coordinates.info.json
new file mode 100644
index 0000000000..5ef44badba
--- /dev/null
+++ b/test/fixtures/style_parser/image-coordinates.info.json
@@ -0,0 +1,7 @@
+{
+ "default": {
+ "log": [
+ [1, "WARNING", "ParseStyle", "Image coordinates must be an array of four longitude latitude pairs"]
+ ]
+ }
+} \ No newline at end of file
diff --git a/test/fixtures/style_parser/image-coordinates.style.json b/test/fixtures/style_parser/image-coordinates.style.json
new file mode 100644
index 0000000000..51b0e93ee7
--- /dev/null
+++ b/test/fixtures/style_parser/image-coordinates.style.json
@@ -0,0 +1,14 @@
+{
+ "version": 8,
+ "center" : [ 75.123, 81.123],
+ "sources": {
+ "image": {
+ "type": "image",
+ "url": "local://0.png",
+ "coordinates": [
+ [ 12.2344, 87.23434],
+ [-12.234, 12.41242]
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/test/fixtures/style_parser/image-url.info.json b/test/fixtures/style_parser/image-url.info.json
new file mode 100644
index 0000000000..988e89c011
--- /dev/null
+++ b/test/fixtures/style_parser/image-url.info.json
@@ -0,0 +1,7 @@
+{
+ "default": {
+ "log": [
+ [1, "WARNING", "ParseStyle", "Image source must have a url value"]
+ ]
+ }
+} \ No newline at end of file
diff --git a/test/fixtures/style_parser/image-url.style.json b/test/fixtures/style_parser/image-url.style.json
new file mode 100644
index 0000000000..94614cb7ab
--- /dev/null
+++ b/test/fixtures/style_parser/image-url.style.json
@@ -0,0 +1,9 @@
+{
+ "version": 8,
+ "center" : [ 75.123, 81.123],
+ "sources": {
+ "image": {
+ "type": "image"
+ }
+ }
+} \ No newline at end of file
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/geometry/binpack.test.cpp b/test/geometry/binpack.test.cpp
deleted file mode 100644
index 0b74df7fa9..0000000000
--- a/test/geometry/binpack.test.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include <mbgl/test/util.hpp>
-
-#include <mbgl/geometry/binpack.hpp>
-
-#include <iosfwd>
-#include <array>
-
-namespace mbgl {
-template <typename T> ::std::ostream& operator<<(::std::ostream& os, const Rect<T>& t) {
- return os << "Rect { " << t.x << ", " << t.y << ", " << t.w << ", " << t.h << " }";
-}
-} // namespace mbgl
-
-TEST(BinPack, Allocating) {
- mbgl::BinPack<uint16_t> bin(128, 128);
- std::array<mbgl::Rect<uint16_t>, 4> rects;
-
- rects[0] = bin.allocate(32, 48);
- ASSERT_EQ(mbgl::Rect<uint16_t>(0, 0, 32, 48), rects[0]);
- rects[1] = bin.allocate(8, 17);
- ASSERT_EQ(mbgl::Rect<uint16_t>(32, 0, 8, 17), rects[1]);
- rects[2] = bin.allocate(8, 17);
- ASSERT_EQ(mbgl::Rect<uint16_t>(0, 48, 8, 17), rects[2]);
-
- bin.release(rects[0]);
- rects[0] = bin.allocate(32, 24);
- ASSERT_EQ(mbgl::Rect<uint16_t>(0, 0, 32, 24), rects[0]);
- rects[3] = bin.allocate(32, 24);
- ASSERT_EQ(mbgl::Rect<uint16_t>(32, 17, 32, 24), rects[3]);
-}
-
-
-TEST(BinPack, Full) {
- mbgl::BinPack<uint16_t> bin(128, 128);
- std::vector<mbgl::Rect<uint16_t>> rects;
-
- for (int j = 0; j < 3; j++) {
- for (int i = 0; i < 256; i++) {
- auto rect = bin.allocate(8, 8);
- ASSERT_TRUE(rect.hasArea());
- rects.push_back(rect);
- }
-
- ASSERT_FALSE(bin.allocate(8, 8).hasArea());
-
- for (auto& rect: rects) {
- bin.release(rect);
- }
- rects.clear();
- }
-}
diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp
index e21d82321d..5f9626bc99 100644
--- a/test/gl/bucket.test.cpp
+++ b/test/gl/bucket.test.cpp
@@ -1,29 +1,94 @@
#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_geometry_tile_feature.hpp>
-#include <mbgl/renderer/circle_bucket.hpp>
-#include <mbgl/renderer/fill_bucket.hpp>
-#include <mbgl/renderer/line_bucket.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/renderer/buckets/circle_bucket.hpp>
+#include <mbgl/renderer/buckets/fill_bucket.hpp>
+#include <mbgl/renderer/buckets/line_bucket.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
+#include <mbgl/gl/context.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 }, {} };
+ gl::Context context;
+ CircleBucket bucket { { {0, 0, 0}, MapMode::Still, 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 }, {} };
+ gl::Context context;
+ FillBucket bucket { { {0, 0, 0}, MapMode::Still, 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 }, {}, {} };
+ gl::Context context;
+ LineBucket bucket { { {0, 0, 0}, MapMode::Still, 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) {
@@ -31,8 +96,180 @@ TEST(Buckets, SymbolBucket) {
bool sdfIcons = false;
bool iconsNeedLinear = false;
+ gl::Context context;
SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear };
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) {
+ gl::Context context;
+ 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);
+ ASSERT_FALSE(bucket.needsUpload());
+
+ 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/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 c24f736fcd..50d5e50abb 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>
@@ -16,6 +14,7 @@
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/async_task.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/util/color.hpp>
@@ -24,87 +23,116 @@ 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 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::Still)
+ : frontend(pixelRatio, fileSource, threadPool)
+ , map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mode) {
+ }
+
+ template <typename T = FileSource>
+ MapTest(const std::string& cachePath, const std::string& assetRoot,
+ float pixelRatio = 1, MapMode mode = MapMode::Still,
+ 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);
-
- map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ 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;
- 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, {});
ASSERT_TRUE(bounds.contains(*virtualCamera.center));
}
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;
@@ -114,39 +142,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.setStyleURL(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.setStyleJSON("invalid");
+ MapTest<> test;
+ test.observer.didFailLoadingMapCallback = [&]() {
+ fail = true;
+ };
+ test.map.getStyle().loadJSON("invalid");
}
EXPECT_TRUE(fail);
@@ -160,7 +199,7 @@ TEST(Map, SetStyleInvalidJSON) {
}
TEST(Map, SetStyleInvalidURL) {
- MapTest test;
+ MapTest<> test;
test.fileSource.styleResponse = [] (const Resource&) {
Response response;
@@ -170,40 +209,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.setStyleURL("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.setStyleJSON("");
- map.setStyleJSON("");
+ 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.setStyleURL("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) {
@@ -211,26 +246,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.setStyleURL("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.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.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) {
@@ -238,72 +271,111 @@ TEST(Map, StyleExpiredWithAnnotations) {
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.setStyleURL("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.map.addAnnotation(LineAnnotation { LineString<double> {{ { 0, 0 }, { 10, 10 } }} });
+ 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, StyleExpiredWithRender) {
+ // Rendering should not prevent revalidation of an expired style.
+
+ using namespace std::chrono_literals;
+
+ 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.frontend.render(test.map);
+ EXPECT_EQ(1u, test.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.setStyleURL("mapbox://styles/test");
- map.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, test.fileSource.requests.size());
+ EXPECT_NE(nullptr, test.map.getStyle().getLayer("water"));
+}
+
+TEST(Map, MapLoadingSignal) {
+ MapTest<> test;
+
+ bool emitted = false;
+ test.observer.onWillStartLoadingMapCallback = [&]() {
+ emitted = true;
+ };
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ EXPECT_TRUE(emitted);
+}
+
+TEST(Map, MapLoadedSignal) {
+ MapTest<> test { 1, MapMode::Continuous };
+
+ test.observer.onDidFinishLoadingMapCallback = [&]() {
+ test.runLoop.stop();
+ };
- EXPECT_EQ(0u, fileSource.requests.size());
- EXPECT_NE(nullptr, map.getLayer("water"));
+ 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.setStyleJSON(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.setStyleJSON("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.setStyleURL("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();
};
@@ -311,21 +383,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.setStyleURL("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();
});
@@ -334,52 +404,48 @@ TEST(Map, TEST_REQUIRES_SERVER(StyleNotFound)) {
test.runLoop.run();
// Should also not retry if the response has cache headers.
- map.setStyleURL("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.setStyleJSON(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.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;
-
- test.backend.getContext().disableVAOExtension = true;
+ MapTest<DefaultFileSource> test { ":memory:", "test/fixtures/api/assets" };
- DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
+ BackendScope scope { *test.frontend.getBackend() };
+ test.frontend.getBackend()->getContext().disableVAOExtension = true;
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.setStyleJSON(util::read_file("test/fixtures/api/water.json"));
+ test.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.setStyleJSON(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.addLayer(std::move(layer));
- map.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> {
@@ -392,15 +458,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.setStyleJSON(R"STYLE(
+ test.map.getStyle().loadJSON(R"STYLE(
{
"version": 8,
"name": "Test",
@@ -434,92 +499,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(Map, Classes) {
- MapTest test;
-
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
-
- EXPECT_FALSE(map.getTransitionOptions().duration);
-
- auto duration = mbgl::Duration(mbgl::Milliseconds(300));
- map.setTransitionOptions({ duration });
- EXPECT_EQ(map.getTransitionOptions().duration, duration);
-
- map.addClass("test");
- EXPECT_TRUE(map.hasClass("test"));
-
- map.removeClass("test");
- EXPECT_TRUE(map.getClasses().empty());
-
- std::vector<std::string> classes = { "foo", "bar" };
- map.setClasses(classes);
- EXPECT_FALSE(map.hasClass("test"));
- EXPECT_TRUE(map.hasClass("foo"));
- EXPECT_TRUE(map.hasClass("bar"));
-
- // Does nothing - same style JSON.
- map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- EXPECT_TRUE(map.hasClass("foo"));
- EXPECT_EQ(map.getTransitionOptions().duration, duration);
-
- map.setStyleJSON(util::read_file("test/fixtures/api/water.json"));
- EXPECT_TRUE(map.getClasses().empty());
- EXPECT_FALSE(map.getTransitionOptions().duration);
-}
-
-TEST(Map, AddImage) {
- MapTest test;
-
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- auto decoded1 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
- auto decoded2 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
- auto image1 = std::make_unique<style::Image>(std::move(decoded1), 1.0);
- auto image2 = std::make_unique<style::Image>(std::move(decoded2), 1.0);
-
- // No-op.
- map.addImage("test-icon", std::move(image1));
-
- map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json"));
- map.addImage("test-icon", std::move(image2));
- test::checkImage("test/fixtures/map/add_icon", test::render(map, test.view));
-}
-
-TEST(Map, RemoveImage) {
- MapTest test;
-
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- auto decoded = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
- auto image = std::make_unique<style::Image>(std::move(decoded), 1.0);
-
- map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json"));
- map.addImage("test-icon", std::move(image));
- map.removeImage("test-icon");
- test::checkImage("test/fixtures/map/remove_icon", test::render(map, test.view));
-}
-
-TEST(Map, GetImage) {
- MapTest test;
-
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- auto decoded = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
- auto image = std::make_unique<style::Image>(std::move(decoded), 1.0);
-
- map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json"));
- map.addImage("test-icon", std::move(image));
- test::checkImage("test/fixtures/map/get_icon", map.getImage("test-icon")->image);
+ 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.setStyleJSON(R"STYLE({
+ test.map.getStyle().loadJSON(R"STYLE({
"sources": {
"a": { "type": "vector", "tiles": [ "a/{z}/{x}/{y}" ] }
},
@@ -557,65 +545,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.setStyleJSON(util::read_file("test/fixtures/api/water.json"));
- util::RunLoop::Get()->run();
+ Map map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Continuous);
+ map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
+
+ 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..5c9a22d1bf
--- /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::Still);
+
+ 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/symbol_program.test.cpp b/test/programs/symbol_program.test.cpp
new file mode 100644
index 0000000000..62a2e58d7b
--- /dev/null
+++ b/test/programs/symbol_program.test.cpp
@@ -0,0 +1,57 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/programs/symbol_program.hpp>
+
+using namespace mbgl;
+
+TEST(SymbolProgram, SymbolSizeBinder) {
+ auto binder = SymbolSizeBinder::create(5.0f, 12.0f, 0.0f);
+ auto uniformValues = binder->uniformValues(5.5f);
+ 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);
+
+ binder = SymbolSizeBinder::create(1.0f, style::CameraFunction<float>(style::ExponentialStops<float>({
+ {0.0f, 8.0f},
+ {10.0f, 18.0f}
+ }, 1.0f)), 0.0f);
+ uniformValues = binder->uniformValues(1.5f);
+ 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);
+
+ binder = SymbolSizeBinder::create(0.0f, style::CameraFunction<float>(style::ExponentialStops<float>({
+ {1.0f, 8.0f},
+ {11.0f, 18.0f}
+ }, 1.0f)), 0.0f);
+ uniformValues = binder->uniformValues(0.5f);
+ 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);
+
+ binder = SymbolSizeBinder::create(12.0f, style::CameraFunction<float>(style::ExponentialStops<float>({
+ {1.0f, 8.0f},
+ {11.0f, 18.0f}
+ }, 1.0f)), 0.0f);
+ uniformValues = binder->uniformValues(12.5f);
+ 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);
+
+ binder = SymbolSizeBinder::create(0.0f, style::SourceFunction<float>("x", style::ExponentialStops<float>({
+ {1.0f, 8.0f},
+ {11.0f, 18.0f}
+ }, 1.0f)), 0.0f);
+ uniformValues = binder->uniformValues(12.5f);
+ EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, true);
+ EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, false);
+
+ binder = SymbolSizeBinder::create(5.0f, style::CompositeFunction<float>("x", style::CompositeExponentialStops<float>({
+ {1.0f, {{0.0f, 8.0f}, {100.0f, 18.0f}}},
+ {11.0f, {{0.0f, 12.0f}, {100.0f, 24.9f}}}
+ }, 1.0f)), 0.0f);
+ uniformValues = binder->uniformValues(5.5f);
+ EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false);
+ EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, false);
+ EXPECT_EQ(uniformValues.get<uniforms::u_size_t>().t, 0.45f);
+}
diff --git a/test/renderer/backend_scope.test.cpp b/test/renderer/backend_scope.test.cpp
index 6afd8f12ed..66cf88a9c6 100644
--- a/test/renderer/backend_scope.test.cpp
+++ b/test/renderer/backend_scope.test.cpp
@@ -1,14 +1,20 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <functional>
using namespace mbgl;
-class StubBackend: public Backend {
+class StubRendererBackend: public RendererBackend {
public:
+ void bind() override {
+ }
+
+ mbgl::Size getFramebufferSize() const override {
+ return mbgl::Size{};
+ }
void activate() override {
if (activateFunction) activateFunction();
@@ -22,8 +28,6 @@ public:
if (updateAssumedStateFunction) updateAssumedStateFunction();
}
- void invalidate() override {}
-
gl::ProcAddress initializeExtension(const char* ext) override {
if (initializeExtensionFunction) {
return initializeExtensionFunction(ext);
@@ -45,7 +49,7 @@ TEST(BackendScope, SingleScope) {
bool activated;
bool deactivated;
- StubBackend backend;
+ StubRendererBackend backend;
backend.activateFunction = [&] { activated = true; };
backend.deactivateFunction = [&] { deactivated = true; };
@@ -63,7 +67,7 @@ TEST(BackendScope, NestedScopes) {
int activated = 0;
int deactivated = 0;
- StubBackend backend;
+ StubRendererBackend backend;
backend.activateFunction = [&] { activated++; };
backend.deactivateFunction = [&] { deactivated++; };
@@ -87,15 +91,15 @@ TEST(BackendScope, NestedScopes) {
TEST(BackendScope, ChainedScopes) {
bool activatedA = false;
bool activatedB = false;
-
- StubBackend backendA;
+
+ StubRendererBackend backendA;
backendA.activateFunction = [&] { activatedA = true; };
backendA.deactivateFunction = [&] { activatedA = false; };
-
- StubBackend backendB;
+
+ StubRendererBackend backendB;
backendB.activateFunction = [&] { activatedB = true; };
backendB.deactivateFunction = [&] { activatedB = false; };
-
+
{
BackendScope scopeA { backendA };
ASSERT_TRUE(activatedA);
@@ -107,7 +111,7 @@ TEST(BackendScope, ChainedScopes) {
ASSERT_FALSE(activatedB);
ASSERT_TRUE(activatedA);
}
-
+
ASSERT_FALSE(activatedA);
ASSERT_FALSE(activatedB);
}
diff --git a/test/renderer/group_by_layout.test.cpp b/test/renderer/group_by_layout.test.cpp
index 9c8e09e222..958f1bdf24 100644
--- a/test/renderer/group_by_layout.test.cpp
+++ b/test/renderer/group_by_layout.test.cpp
@@ -13,7 +13,7 @@ static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vecto
std::vector<std::unique_ptr<RenderLayer>> result;
result.reserve(layers.size());
for (auto& layer : layers) {
- result.push_back(layer->baseImpl->createRenderLayer());
+ result.push_back(RenderLayer::create(layer->baseImpl));
}
return result;
}
diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp
new file mode 100644
index 0000000000..ebe1bcd72f
--- /dev/null
+++ b/test/renderer/image_manager.test.cpp
@@ -0,0 +1,150 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/fixture_log_observer.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/test/stub_style_observer.hpp>
+
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/sprite/sprite_parser.hpp>
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <utility>
+
+using namespace mbgl;
+
+TEST(ImageManager, Missing) {
+ ImageManager imageManager;
+ EXPECT_FALSE(imageManager.getImage("doesnotexist"));
+}
+
+TEST(ImageManager, Basic) {
+ FixtureLog log;
+ ImageManager imageManager;
+
+ auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
+ util::read_file("test/fixtures/annotations/emerald.json"));
+ for (auto& image : images) {
+ imageManager.addImage(image->baseImpl);
+ }
+
+ auto metro = *imageManager.getPattern("metro");
+ EXPECT_EQ(1, metro.tl()[0]);
+ EXPECT_EQ(1, metro.tl()[1]);
+ EXPECT_EQ(19, metro.br()[0]);
+ EXPECT_EQ(19, metro.br()[1]);
+ EXPECT_EQ(18, metro.displaySize()[0]);
+ EXPECT_EQ(18, metro.displaySize()[1]);
+ EXPECT_EQ(1.0f, metro.pixelRatio);
+ EXPECT_EQ(imageManager.getPixelSize(), imageManager.getAtlasImage().size);
+
+ test::checkImage("test/fixtures/image_manager/basic", imageManager.getAtlasImage());
+}
+
+TEST(ImageManager, Updates) {
+ ImageManager imageManager;
+
+ PremultipliedImage imageA({ 16, 12 });
+ imageA.fill(255);
+ imageManager.addImage(makeMutable<style::Image::Impl>("one", std::move(imageA), 1));
+
+ auto a = *imageManager.getPattern("one");
+ EXPECT_EQ(1, a.tl()[0]);
+ EXPECT_EQ(1, a.tl()[1]);
+ EXPECT_EQ(17, a.br()[0]);
+ EXPECT_EQ(13, a.br()[1]);
+ EXPECT_EQ(16, a.displaySize()[0]);
+ EXPECT_EQ(12, a.displaySize()[1]);
+ EXPECT_EQ(1.0f, a.pixelRatio);
+ test::checkImage("test/fixtures/image_manager/updates_before", imageManager.getAtlasImage());
+
+ PremultipliedImage imageB({ 5, 5 });
+ imageA.fill(200);
+ imageManager.updateImage(makeMutable<style::Image::Impl>("one", std::move(imageB), 1));
+
+ auto b = *imageManager.getPattern("one");
+ EXPECT_EQ(1, b.tl()[0]);
+ EXPECT_EQ(1, b.tl()[1]);
+ EXPECT_EQ(6, b.br()[0]);
+ EXPECT_EQ(6, b.br()[1]);
+ EXPECT_EQ(5, b.displaySize()[0]);
+ EXPECT_EQ(5, b.displaySize()[1]);
+ EXPECT_EQ(1.0f, b.pixelRatio);
+ test::checkImage("test/fixtures/image_manager/updates_after", imageManager.getAtlasImage());
+}
+
+TEST(ImageManager, AddRemove) {
+ FixtureLog log;
+ ImageManager imageManager;
+
+ imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2));
+ imageManager.addImage(makeMutable<style::Image::Impl>("two", PremultipliedImage({ 16, 16 }), 2));
+ imageManager.addImage(makeMutable<style::Image::Impl>("three", PremultipliedImage({ 16, 16 }), 2));
+
+ imageManager.removeImage("one");
+ imageManager.removeImage("two");
+
+ EXPECT_NE(nullptr, imageManager.getImage("three"));
+ EXPECT_EQ(nullptr, imageManager.getImage("two"));
+ EXPECT_EQ(nullptr, imageManager.getImage("four"));
+}
+
+TEST(ImageManager, RemoveReleasesBinPackRect) {
+ FixtureLog log;
+ ImageManager imageManager;
+
+ imageManager.addImage(makeMutable<style::Image::Impl>("big", PremultipliedImage({ 32, 32 }), 1));
+ EXPECT_TRUE(imageManager.getImage("big"));
+
+ imageManager.removeImage("big");
+
+ imageManager.addImage(makeMutable<style::Image::Impl>("big", PremultipliedImage({ 32, 32 }), 1));
+ EXPECT_TRUE(imageManager.getImage("big"));
+ EXPECT_TRUE(log.empty());
+}
+
+class StubImageRequestor : public ImageRequestor {
+public:
+ 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) {
+ ImageManager imageManager;
+ StubImageRequestor requestor;
+ bool notified = false;
+
+ requestor.imagesAvailable = [&] (ImageMap) {
+ notified = true;
+ };
+
+ uint64_t imageCorrelationID = 0;
+ imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID));
+ ASSERT_FALSE(notified);
+
+ imageManager.setLoaded(true);
+ ASSERT_TRUE(notified);
+}
+
+TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) {
+ ImageManager imageManager;
+ StubImageRequestor requestor;
+ bool notified = false;
+
+ requestor.imagesAvailable = [&] (ImageMap) {
+ notified = true;
+ };
+
+ uint64_t imageCorrelationID = 0;
+ imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2));
+ imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID));
+
+ ASSERT_TRUE(notified);
+}
diff --git a/test/sprite/sprite_atlas.test.cpp b/test/sprite/sprite_atlas.test.cpp
deleted file mode 100644
index 08388f0a93..0000000000
--- a/test/sprite/sprite_atlas.test.cpp
+++ /dev/null
@@ -1,373 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/test/fixture_log_observer.hpp>
-#include <mbgl/test/stub_file_source.hpp>
-#include <mbgl/test/stub_style_observer.hpp>
-
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/sprite/sprite_parser.hpp>
-#include <mbgl/util/io.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/default_thread_pool.hpp>
-#include <mbgl/util/string.hpp>
-
-#include <utility>
-
-using namespace mbgl;
-
-TEST(SpriteAtlas, Basic) {
- FixtureLog log;
- SpriteAtlas atlas({ 63, 112 }, 1);
-
- auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
- util::read_file("test/fixtures/annotations/emerald.json"));
- for (auto& pair : images) {
- atlas.addImage(pair.first, std::move(pair.second));
- }
-
- EXPECT_EQ(1.0f, atlas.getPixelRatio());
- EXPECT_EQ(63u, atlas.getSize().width);
- EXPECT_EQ(112u, atlas.getSize().height);
-
- auto metro = *atlas.getIcon("metro");
- float imagePixelRatio = metro.relativePixelRatio * atlas.getPixelRatio();
- EXPECT_EQ(0, metro.pos.x);
- EXPECT_EQ(0, metro.pos.y);
- EXPECT_EQ(20, metro.pos.w);
- EXPECT_EQ(20, metro.pos.h);
- EXPECT_EQ(18, metro.width);
- EXPECT_EQ(18, metro.height);
- EXPECT_EQ(18u, metro.width * imagePixelRatio);
- EXPECT_EQ(18u, metro.height * imagePixelRatio);
- EXPECT_EQ(1.0f, imagePixelRatio);
-
-
- EXPECT_EQ(63u, atlas.getAtlasImage().size.width);
- EXPECT_EQ(112u, atlas.getAtlasImage().size.height);
-
- auto pos = *atlas.getIcon("metro");
- EXPECT_DOUBLE_EQ(18, pos.size[0]);
- EXPECT_DOUBLE_EQ(18, pos.size[1]);
- EXPECT_DOUBLE_EQ(1.0f / 63, pos.tl[0]);
- EXPECT_DOUBLE_EQ(1.0f / 112, pos.tl[1]);
- EXPECT_DOUBLE_EQ(19.0f / 63, pos.br[0]);
- EXPECT_DOUBLE_EQ(19.0f / 112, pos.br[1]);
-
- auto missing = atlas.getIcon("doesnotexist");
- EXPECT_FALSE(missing);
-
- EXPECT_EQ(1u, log.count({
- EventSeverity::Info,
- Event::Sprite,
- int64_t(-1),
- "Can't find sprite named 'doesnotexist'",
- }));
-
- // Different wrapping mode produces different image.
- auto metro2 = *atlas.getPattern("metro");
- EXPECT_EQ(20, metro2.pos.x);
- EXPECT_EQ(0, metro2.pos.y);
- EXPECT_EQ(20, metro2.pos.w);
- EXPECT_EQ(20, metro2.pos.h);
-
- test::checkImage("test/fixtures/sprite_atlas/basic", atlas.getAtlasImage());
-}
-
-TEST(SpriteAtlas, Size) {
- SpriteAtlas atlas({ 63, 112 }, 1.4);
-
- auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
- util::read_file("test/fixtures/annotations/emerald.json"));
- for (auto& pair : images) {
- atlas.addImage(pair.first, std::move(pair.second));
- }
-
- EXPECT_DOUBLE_EQ(1.4f, atlas.getPixelRatio());
- EXPECT_EQ(63u, atlas.getSize().width);
- EXPECT_EQ(112u, atlas.getSize().height);
-
- auto metro = *atlas.getIcon("metro");
- float imagePixelRatio = metro.relativePixelRatio * atlas.getPixelRatio();
- EXPECT_EQ(0, metro.pos.x);
- EXPECT_EQ(0, metro.pos.y);
- EXPECT_EQ(16, metro.pos.w);
- EXPECT_EQ(16, metro.pos.h);
- EXPECT_EQ(18, metro.width);
- EXPECT_EQ(18, metro.height);
- EXPECT_EQ(18u, metro.width * imagePixelRatio);
- EXPECT_EQ(18u, metro.height * imagePixelRatio);
- EXPECT_EQ(1.0f, imagePixelRatio);
-
- // Now the image was created lazily.
- EXPECT_EQ(89u, atlas.getAtlasImage().size.width);
- EXPECT_EQ(157u, atlas.getAtlasImage().size.height);
-
- test::checkImage("test/fixtures/sprite_atlas/size", atlas.getAtlasImage());
-}
-
-TEST(SpriteAtlas, Updates) {
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- EXPECT_EQ(1.0f, atlas.getPixelRatio());
- EXPECT_EQ(32u, atlas.getSize().width);
- EXPECT_EQ(32u, atlas.getSize().height);
-
- atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 16, 12 }), 1));
- auto one = *atlas.getIcon("one");
- float imagePixelRatio = one.relativePixelRatio * atlas.getPixelRatio();
- EXPECT_EQ(0, one.pos.x);
- EXPECT_EQ(0, one.pos.y);
- EXPECT_EQ(20, one.pos.w);
- EXPECT_EQ(16, one.pos.h);
- EXPECT_EQ(16, one.width);
- EXPECT_EQ(12, one.height);
- EXPECT_EQ(16u, one.width * imagePixelRatio);
- EXPECT_EQ(12u, one.height * imagePixelRatio);
- EXPECT_EQ(1.0f, imagePixelRatio);
-
- // Now the image was created lazily.
- EXPECT_EQ(32u, atlas.getAtlasImage().size.width);
- EXPECT_EQ(32u, atlas.getAtlasImage().size.height);
-
- test::checkImage("test/fixtures/sprite_atlas/updates_before", atlas.getAtlasImage());
-
- // Update image
- PremultipliedImage image2({ 16, 12 });
- for (size_t i = 0; i < image2.bytes(); i++) {
- image2.data.get()[i] = 255;
- }
- atlas.addImage("one", std::make_unique<style::Image>(std::move(image2), 1));
-
- test::checkImage("test/fixtures/sprite_atlas/updates_after", atlas.getAtlasImage());
-}
-
-TEST(SpriteAtlas, AddRemove) {
- FixtureLog log;
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- atlas.addImage("two", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- atlas.addImage("three", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
-
- atlas.removeImage("one");
- atlas.removeImage("two");
-
- EXPECT_NE(nullptr, atlas.getImage("three"));
- EXPECT_EQ(nullptr, atlas.getImage("two"));
- EXPECT_EQ(nullptr, atlas.getImage("four"));
-
- EXPECT_EQ(1u, log.count({
- EventSeverity::Info,
- Event::Sprite,
- int64_t(-1),
- "Can't find sprite named 'two'",
- }));
- EXPECT_EQ(1u, log.count({
- EventSeverity::Info,
- Event::Sprite,
- int64_t(-1),
- "Can't find sprite named 'four'",
- }));
-}
-
-TEST(SpriteAtlas, RemoveReleasesBinPackRect) {
- FixtureLog log;
-
- SpriteAtlas atlas({ 36, 36 }, 1);
-
- atlas.addImage("big", std::make_unique<style::Image>(PremultipliedImage({ 32, 32 }), 1));
- EXPECT_TRUE(atlas.getIcon("big"));
-
- atlas.removeImage("big");
-
- atlas.addImage("big", std::make_unique<style::Image>(PremultipliedImage({ 32, 32 }), 1));
- EXPECT_TRUE(atlas.getIcon("big"));
- EXPECT_TRUE(log.empty());
-}
-
-TEST(SpriteAtlas, OtherPixelRatio) {
- FixtureLog log;
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- // Adding mismatched sprite image
- atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 8, 8 }), 2));
-}
-
-TEST(SpriteAtlas, Replace) {
- FixtureLog log;
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- auto image = atlas.getImage("sprite");
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- EXPECT_NE(image, atlas.getImage("sprite"));
-}
-
-TEST(SpriteAtlas, ReplaceWithDifferentDimensions) {
- FixtureLog log;
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 18, 18 }), 2));
-
- EXPECT_EQ(1u, log.count({
- EventSeverity::Warning,
- Event::Sprite,
- int64_t(-1),
- "Can't change sprite dimensions for 'sprite'",
- }));
-}
-
-class SpriteAtlasTest {
-public:
- SpriteAtlasTest() = default;
-
- util::RunLoop loop;
- StubFileSource fileSource;
- StubStyleObserver observer;
- ThreadPool threadPool { 1 };
- SpriteAtlas spriteAtlas{ { 32, 32 }, 1 };
-
- void run() {
- // Squelch logging.
- Log::setObserver(std::make_unique<Log::NullObserver>());
-
- spriteAtlas.setObserver(&observer);
- spriteAtlas.load("test/fixtures/resources/sprite", threadPool, fileSource);
-
- loop.run();
- }
-
- void end() {
- loop.stop();
- }
-};
-
-Response successfulSpriteImageResponse(const Resource& resource) {
- EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url);
- Response response;
- response.data = std::make_unique<std::string>(util::read_file(resource.url));
- return response;
-}
-
-Response successfulSpriteJSONResponse(const Resource& resource) {
- EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url);
- Response response;
- response.data = std::make_unique<std::string>(util::read_file(resource.url));
- return response;
-}
-
-Response failedSpriteResponse(const Resource&) {
- Response response;
- response.error = std::make_unique<Response::Error>(
- Response::Error::Reason::Other,
- "Failed by the test case");
- return response;
-}
-
-Response corruptSpriteResponse(const Resource&) {
- Response response;
- response.data = std::make_unique<std::string>("CORRUPT");
- return response;
-}
-
-TEST(SpriteAtlas, LoadingSuccess) {
- SpriteAtlasTest test;
-
- test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
- test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- FAIL() << util::toString(error);
- test.end();
- };
-
- test.observer.spriteLoaded = [&] () {
- EXPECT_EQ(1.0, test.spriteAtlas.getPixelRatio());
- EXPECT_TRUE(test.spriteAtlas.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteAtlas, JSONLoadingFail) {
- SpriteAtlasTest test;
-
- test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
- test.fileSource.spriteJSONResponse = failedSpriteResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ("Failed by the test case", util::toString(error));
- EXPECT_FALSE(test.spriteAtlas.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteAtlas, ImageLoadingFail) {
- SpriteAtlasTest test;
-
- test.fileSource.spriteImageResponse = failedSpriteResponse;
- test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ("Failed by the test case", util::toString(error));
- EXPECT_FALSE(test.spriteAtlas.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteAtlas, JSONLoadingCorrupted) {
- SpriteAtlasTest test;
-
- test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
- test.fileSource.spriteJSONResponse = corruptSpriteResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error));
- EXPECT_FALSE(test.spriteAtlas.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteAtlas, ImageLoadingCorrupted) {
- SpriteAtlasTest test;
-
- test.fileSource.spriteImageResponse = corruptSpriteResponse;
- test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- // Not asserting on platform-specific error text.
- EXPECT_FALSE(test.spriteAtlas.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteAtlas, LoadingCancel) {
- SpriteAtlasTest test;
-
- test.fileSource.spriteImageResponse =
- test.fileSource.spriteJSONResponse = [&] (const Resource&) {
- test.end();
- return optional<Response>();
- };
-
- test.observer.spriteLoaded = [&] () {
- FAIL() << "Should never be called";
- };
-
- test.run();
-}
diff --git a/test/sprite/sprite_loader.test.cpp b/test/sprite/sprite_loader.test.cpp
new file mode 100644
index 0000000000..3691572265
--- /dev/null
+++ b/test/sprite/sprite_loader.test.cpp
@@ -0,0 +1,179 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/fixture_log_observer.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+
+#include <mbgl/sprite/sprite_loader.hpp>
+#include <mbgl/sprite/sprite_loader_observer.hpp>
+#include <mbgl/sprite/sprite_parser.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <utility>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+class StubSpriteLoaderObserver : public SpriteLoaderObserver {
+public:
+ void onSpriteLoaded(std::vector<std::unique_ptr<style::Image>>&& images) override {
+ if (spriteLoaded) spriteLoaded(std::move(images));
+ }
+
+ void onSpriteError(std::exception_ptr error) override {
+ if (spriteError) spriteError(error);
+ }
+
+ std::function<void (std::vector<std::unique_ptr<style::Image>>&&)> spriteLoaded;
+ std::function<void (std::exception_ptr)> spriteError;
+};
+
+class SpriteLoaderTest {
+public:
+ SpriteLoaderTest() = default;
+
+ util::RunLoop loop;
+ StubFileSource fileSource;
+ StubSpriteLoaderObserver observer;
+ ThreadPool threadPool { 1 };
+ SpriteLoader spriteLoader{ 1 };
+
+ void run() {
+ // Squelch logging.
+ Log::setObserver(std::make_unique<Log::NullObserver>());
+
+ spriteLoader.setObserver(&observer);
+ spriteLoader.load("test/fixtures/resources/sprite", threadPool, fileSource);
+
+ loop.run();
+ }
+
+ void end() {
+ loop.stop();
+ }
+};
+
+Response successfulSpriteImageResponse(const Resource& resource) {
+ EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url);
+ Response response;
+ response.data = std::make_unique<std::string>(util::read_file(resource.url));
+ return response;
+}
+
+Response successfulSpriteJSONResponse(const Resource& resource) {
+ EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url);
+ Response response;
+ response.data = std::make_unique<std::string>(util::read_file(resource.url));
+ return response;
+}
+
+Response failedSpriteResponse(const Resource&) {
+ Response response;
+ response.error = std::make_unique<Response::Error>(
+ Response::Error::Reason::Other,
+ "Failed by the test case");
+ return response;
+}
+
+Response corruptSpriteResponse(const Resource&) {
+ Response response;
+ response.data = std::make_unique<std::string>("CORRUPT");
+ return response;
+}
+
+TEST(SpriteLoader, LoadingSuccess) {
+ SpriteLoaderTest test;
+
+ test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
+ test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ FAIL() << util::toString(error);
+ test.end();
+ };
+
+ test.observer.spriteLoaded = [&] (std::vector<std::unique_ptr<style::Image>>&& images) {
+ EXPECT_EQ(images.size(), 367u);
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, JSONLoadingFail) {
+ SpriteLoaderTest test;
+
+ test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
+ test.fileSource.spriteJSONResponse = failedSpriteResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ("Failed by the test case", util::toString(error));
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, ImageLoadingFail) {
+ SpriteLoaderTest test;
+
+ test.fileSource.spriteImageResponse = failedSpriteResponse;
+ test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ("Failed by the test case", util::toString(error));
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, JSONLoadingCorrupted) {
+ SpriteLoaderTest test;
+
+ test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
+ test.fileSource.spriteJSONResponse = corruptSpriteResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error));
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, ImageLoadingCorrupted) {
+ SpriteLoaderTest test;
+
+ test.fileSource.spriteImageResponse = corruptSpriteResponse;
+ test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ // Not asserting on platform-specific error text.
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, LoadingCancel) {
+ SpriteLoaderTest test;
+
+ test.fileSource.spriteImageResponse =
+ test.fileSource.spriteJSONResponse = [&] (const Resource&) {
+ test.end();
+ return optional<Response>();
+ };
+
+ test.observer.spriteLoaded = [&] (const std::vector<std::unique_ptr<style::Image>>&) {
+ FAIL() << "Should never be called";
+ };
+
+ test.run();
+}
diff --git a/test/sprite/sprite_parser.test.cpp b/test/sprite/sprite_parser.test.cpp
index bb8e71db95..529e4c75e8 100644
--- a/test/sprite/sprite_parser.test.cpp
+++ b/test/sprite/sprite_parser.test.cpp
@@ -27,19 +27,19 @@ TEST(Sprite, SpriteImageCreationInvalid) {
ASSERT_EQ(200u, image_1x.size.width);
ASSERT_EQ(299u, image_1x.size.height);
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 0, 16, 1, false)); // width == 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, 0, 1, false)); // height == 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, -1, 16, 1, false)); // width < 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, -1, 1, false)); // height < 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 1, 1, 0, false)); // ratio == 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 1, 1, -1, false)); // ratio < 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 1, 1, 23, false)); // ratio too large
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 2048, 16, 1, false)); // too wide
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, 1025, 1, false)); // too tall
- ASSERT_EQ(nullptr, createStyleImage(image_1x, -1, 0, 16, 16, 1, false)); // srcX < 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, -1, 16, 16, 1, false)); // srcY < 0
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, image_1x.size.width + 1, 16, 1, false)); // right edge out of bounds
- ASSERT_EQ(nullptr, createStyleImage(image_1x, 0, 0, 16, image_1x.size.height + 1, 1, false)); // bottom edge out of bounds
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 0, 16, 1, false)); // width == 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, 0, 1, false)); // height == 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, -1, 16, 1, false)); // width < 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, -1, 1, false)); // height < 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 1, 1, 0, false)); // ratio == 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 1, 1, -1, false)); // ratio < 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 1, 1, 23, false)); // ratio too large
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 2048, 16, 1, false)); // too wide
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, 1025, 1, false)); // too tall
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, -1, 0, 16, 16, 1, false)); // srcX < 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, -1, 16, 16, 1, false)); // srcY < 0
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, image_1x.size.width + 1, 16, 1, false)); // right edge out of bounds
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, image_1x.size.height + 1, 1, false)); // bottom edge out of bounds
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
@@ -141,15 +141,13 @@ TEST(Sprite, SpriteImageCreation1x) {
ASSERT_EQ(299u, image_1x.size.height);
{ // "museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false}
- const auto sprite = createStyleImage(image_1x, 177, 187, 18, 18, 1, false);
+ const auto sprite = createStyleImage("test", image_1x, 177, 187, 18, 18, 1, false);
ASSERT_TRUE(sprite.get());
- EXPECT_EQ(18, sprite->getWidth());
- EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(18u, sprite->image.size.width);
- EXPECT_EQ(18u, sprite->image.size.height);
- EXPECT_EQ(1, sprite->pixelRatio);
+ EXPECT_EQ(18u, sprite->getImage().size.width);
+ EXPECT_EQ(18u, sprite->getImage().size.height);
+ EXPECT_EQ(1, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1x-museum.png"),
- sprite->image);
+ sprite->getImage());
}
}
@@ -157,41 +155,35 @@ TEST(Sprite, SpriteImageCreation2x) {
const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png"));
// "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false}
- const auto sprite = createStyleImage(image_2x, 354, 374, 36, 36, 2, false);
+ const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 2, false);
ASSERT_TRUE(sprite.get());
- EXPECT_EQ(18, sprite->getWidth());
- EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(36u, sprite->image.size.width);
- EXPECT_EQ(36u, sprite->image.size.height);
- EXPECT_EQ(2, sprite->pixelRatio);
+ EXPECT_EQ(36u, sprite->getImage().size.width);
+ EXPECT_EQ(36u, sprite->getImage().size.height);
+ EXPECT_EQ(2, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation2x.png"),
- sprite->image);
+ sprite->getImage());
}
TEST(Sprite, SpriteImageCreation1_5x) {
const PremultipliedImage image_2x = decodeImage(util::read_file("test/fixtures/annotations/emerald@2x.png"));
// "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false}
- const auto sprite = createStyleImage(image_2x, 354, 374, 36, 36, 1.5, false);
+ const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 1.5, false);
ASSERT_TRUE(sprite.get());
- EXPECT_EQ(24, sprite->getWidth());
- EXPECT_EQ(24, sprite->getHeight());
- EXPECT_EQ(36u, sprite->image.size.width);
- EXPECT_EQ(36u, sprite->image.size.height);
- EXPECT_EQ(1.5, sprite->pixelRatio);
+ EXPECT_EQ(36u, sprite->getImage().size.width);
+ EXPECT_EQ(36u, sprite->getImage().size.height);
+ EXPECT_EQ(1.5, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-museum.png"),
- sprite->image);
+ sprite->getImage());
// "hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false}
- const auto sprite2 = createStyleImage(image_2x, 314, 518, 35, 35, 1.5, false);
+ const auto sprite2 = createStyleImage("test", image_2x, 314, 518, 35, 35, 1.5, false);
ASSERT_TRUE(sprite2.get());
- EXPECT_EQ(float(35 / 1.5), sprite2->getWidth());
- EXPECT_EQ(float(35 / 1.5), sprite2->getHeight());
- EXPECT_EQ(35u, sprite2->image.size.width);
- EXPECT_EQ(35u, sprite2->image.size.height);
- EXPECT_EQ(1.5, sprite2->pixelRatio);
+ EXPECT_EQ(35u, sprite2->getImage().size.width);
+ EXPECT_EQ(35u, sprite2->getImage().size.height);
+ EXPECT_EQ(1.5, sprite2->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-hospital.png"),
- sprite2->image);
+ sprite2->getImage());
}
TEST(Sprite, SpriteParsing) {
@@ -202,7 +194,7 @@ TEST(Sprite, SpriteParsing) {
std::set<std::string> names;
std::transform(images.begin(), images.end(), std::inserter(names, names.begin()),
- [](const auto& pair) { return pair.first; });
+ [](const auto& image) { return image->getID(); });
EXPECT_EQ(std::set<std::string>({ "airfield_icon",
"airport_icon",
@@ -280,13 +272,11 @@ TEST(Sprite, SpriteParsing) {
names);
{
- auto& sprite = images.find("generic-metro")->second;
- EXPECT_EQ(18, sprite->getWidth());
- EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(18u, sprite->image.size.width);
- EXPECT_EQ(18u, sprite->image.size.height);
- EXPECT_EQ(1, sprite->pixelRatio);
- EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->image);
+ auto& sprite = *std::find_if(images.begin(), images.end(), [] (const auto& image) { return image->getID() == "generic-metro"; });
+ EXPECT_EQ(18u, sprite->getImage().size.width);
+ EXPECT_EQ(18u, sprite->getImage().size.height);
+ EXPECT_EQ(1, sprite->getPixelRatio());
+ EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->getImage());
}
}
diff --git a/test/src/mbgl/test/conversion_stubs.hpp b/test/src/mbgl/test/conversion_stubs.hpp
index e6581c5e53..30395ddb97 100644
--- a/test/src/mbgl/test/conversion_stubs.hpp
+++ b/test/src/mbgl/test/conversion_stubs.hpp
@@ -17,6 +17,7 @@ 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>> {
@@ -90,6 +91,14 @@ inline optional<float> toNumber(const Value& value) {
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>();
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/fixture_log_observer.cpp b/test/src/mbgl/test/fixture_log_observer.cpp
index fc0239bb1c..717d2da753 100644
--- a/test/src/mbgl/test/fixture_log_observer.cpp
+++ b/test/src/mbgl/test/fixture_log_observer.cpp
@@ -15,9 +15,6 @@ bool FixtureLog::Message::operator==(const Message& rhs) const {
return severity == rhs.severity && event == rhs.event && code == rhs.code && msg == rhs.msg;
}
-FixtureLog::Message::Message() : severity(), event(), code(), msg() {
-}
-
FixtureLog::Observer::Observer(FixtureLog* log_) : log(log_) {
}
@@ -97,10 +94,10 @@ std::vector<FixtureLog::Message> FixtureLogObserver::unchecked() const {
}
::std::ostream& operator<<(::std::ostream& os, const FixtureLog::Message& message) {
- os << "[\"" << Enum<EventSeverity>::toString(message.severity) << "\", \"";
- os << Enum<Event>::toString(message.event) << "\"";
+ os << R"([")" << Enum<EventSeverity>::toString(message.severity) << R"(", ")";
+ os << Enum<Event>::toString(message.event) << R"(")";
os << ", " << message.code;
- os << ", \"" << message.msg << "\"";
+ os << R"(, ")" << message.msg << R"(")";
return os << "]" << std::endl;
}
diff --git a/test/src/mbgl/test/fixture_log_observer.hpp b/test/src/mbgl/test/fixture_log_observer.hpp
index 96ddc2c54f..328d4753a8 100644
--- a/test/src/mbgl/test/fixture_log_observer.hpp
+++ b/test/src/mbgl/test/fixture_log_observer.hpp
@@ -12,15 +12,15 @@ namespace mbgl {
class FixtureLog {
public:
struct Message {
+ Message() = default;
Message(EventSeverity severity_, Event event_, int64_t code_, std::string msg_);
- Message();
bool operator==(const Message& rhs) const;
- const EventSeverity severity;
- const Event event;
- const int64_t code;
- const std::string msg;
+ const EventSeverity severity {};
+ const Event event {};
+ const int64_t code {};
+ const std::string msg {};
mutable bool checked = false;
};
diff --git a/test/src/mbgl/test/getrss.cpp b/test/src/mbgl/test/getrss.cpp
index 9f57ad8e7b..c21b653eaa 100644
--- a/test/src/mbgl/test/getrss.cpp
+++ b/test/src/mbgl/test/getrss.cpp
@@ -80,8 +80,8 @@ size_t getCurrentRSS( )
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
/* Linux ---------------------------------------------------- */
long rss = 0L;
- FILE* fp = NULL;
- if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
+ FILE* fp = nullptr;
+ if ( (fp = fopen( "/proc/self/statm", "r" )) == nullptr )
return (size_t)0L; /* Can't open? */
if ( fscanf( fp, "%*s%ld", &rss ) != 1 )
{
diff --git a/test/src/mbgl/test/getrss.hpp b/test/src/mbgl/test/getrss.hpp
index a4420c4b5f..be45ae889a 100644
--- a/test/src/mbgl/test/getrss.hpp
+++ b/test/src/mbgl/test/getrss.hpp
@@ -41,5 +41,5 @@ size_t getPeakRSS();
*/
size_t getCurrentRSS();
-}
-}
+} // namespace test
+} // namespace mbgl
diff --git a/test/src/mbgl/test/stub_file_source.cpp b/test/src/mbgl/test/stub_file_source.cpp
index ec0545e88c..7891d5d907 100644
--- a/test/src/mbgl/test/stub_file_source.cpp
+++ b/test/src/mbgl/test/stub_file_source.cpp
@@ -77,6 +77,9 @@ optional<Response> StubFileSource::defaultResponse(const Resource& resource) {
case Resource::Kind::SpriteImage:
if (!spriteImageResponse) throw std::runtime_error("unexpected sprite image request");
return spriteImageResponse(resource);
+ case Resource::Kind::Image:
+ if (!imageResponse) throw std::runtime_error("unexpected image request");
+ return imageResponse(resource);
case Resource::Kind::Unknown:
throw std::runtime_error("unknown resource type");
}
diff --git a/test/src/mbgl/test/stub_file_source.hpp b/test/src/mbgl/test/stub_file_source.hpp
index ee4175cc3f..85118e1a77 100644
--- a/test/src/mbgl/test/stub_file_source.hpp
+++ b/test/src/mbgl/test/stub_file_source.hpp
@@ -29,6 +29,7 @@ public:
ResponseFunction glyphsResponse;
ResponseFunction spriteJSONResponse;
ResponseFunction spriteImageResponse;
+ ResponseFunction imageResponse;
private:
// The default behavior is to throw if no per-kind callback has been set.
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/stub_layer_observer.hpp b/test/src/mbgl/test/stub_layer_observer.hpp
index 9acd4b077a..0fa413aefe 100644
--- a/test/src/mbgl/test/stub_layer_observer.hpp
+++ b/test/src/mbgl/test/stub_layer_observer.hpp
@@ -10,29 +10,9 @@ using namespace mbgl::style;
*/
class StubLayerObserver : public style::LayerObserver {
public:
- void onLayerFilterChanged(Layer& layer) override {
- if (layerFilterChanged) layerFilterChanged(layer);
+ void onLayerChanged(Layer& layer) override {
+ if (layerChanged) layerChanged(layer);
}
- void onLayerVisibilityChanged(Layer& layer) override {
- if (layerVisibilityChanged) layerVisibilityChanged(layer);
- }
-
- void onLayerPaintPropertyChanged(Layer& layer) override {
- if (layerPaintPropertyChanged) layerPaintPropertyChanged(layer);
- }
-
- void onLayerDataDrivenPaintPropertyChanged(Layer& layer) override {
- if (layerDataDrivenPaintPropertyChanged) layerDataDrivenPaintPropertyChanged(layer);
- }
-
- void onLayerLayoutPropertyChanged(Layer& layer, const char * property) override {
- if (layerLayoutPropertyChanged) layerLayoutPropertyChanged(layer, property);
- }
-
- std::function<void (Layer&)> layerFilterChanged;
- std::function<void (Layer&)> layerVisibilityChanged;
- std::function<void (Layer&)> layerPaintPropertyChanged;
- std::function<void (Layer&)> layerDataDrivenPaintPropertyChanged;
- std::function<void (Layer&, const char *)> layerLayoutPropertyChanged;
+ std::function<void (Layer&)> layerChanged;
};
diff --git a/test/src/mbgl/test/stub_style_observer.hpp b/test/src/mbgl/test/stub_style_observer.hpp
index 7e22c68823..b97911cdb0 100644
--- a/test/src/mbgl/test/stub_style_observer.hpp
+++ b/test/src/mbgl/test/stub_style_observer.hpp
@@ -10,22 +10,6 @@ using namespace mbgl::style;
*/
class StubStyleObserver : public style::Observer {
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);
- }
-
- void onSpriteLoaded() override {
- if (spriteLoaded) spriteLoaded();
- }
-
- void onSpriteError(std::exception_ptr error) override {
- if (spriteError) spriteError(error);
- }
-
void onSourceLoaded(Source& source) override {
if (sourceLoaded) sourceLoaded(source);
}
@@ -46,10 +30,6 @@ public:
if (resourceError) resourceError(error);
};
- std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded;
- std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError;
- std::function<void ()> spriteLoaded;
- std::function<void (std::exception_ptr)> spriteError;
std::function<void (Source&)> sourceLoaded;
std::function<void (Source&)> sourceChanged;
std::function<void (Source&, std::exception_ptr)> sourceError;
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/asset_file_source.test.cpp b/test/storage/asset_file_source.test.cpp
index 010a2c9dc7..a39d2963d2 100644
--- a/test/storage/asset_file_source.test.cpp
+++ b/test/storage/asset_file_source.test.cpp
@@ -3,8 +3,10 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/thread.hpp>
+#include <mbgl/actor/actor_ref.hpp>
#include <gtest/gtest.h>
+#include <atomic>
using namespace mbgl;
@@ -20,16 +22,11 @@ TEST(AssetFileSource, Load) {
#else
unsigned numThreads = 50;
#endif
-
- auto callback = [&] {
- if (!--numThreads) {
- loop.stop();
- }
- };
+ std::atomic_uint completed(numThreads);
class TestWorker {
public:
- TestWorker(mbgl::AssetFileSource* fs_) : fs(fs_) {}
+ TestWorker(ActorRef<TestWorker>, mbgl::AssetFileSource* fs_) : fs(fs_) {}
void run(std::function<void()> endCallback) {
const std::string asset("asset://nonempty");
@@ -60,16 +57,12 @@ TEST(AssetFileSource, Load) {
};
std::vector<std::unique_ptr<util::Thread<TestWorker>>> threads;
- std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests;
- util::ThreadContext context = { "Test" };
for (unsigned i = 0; i < numThreads; ++i) {
std::unique_ptr<util::Thread<TestWorker>> thread =
- std::make_unique<util::Thread<TestWorker>>(context, &fs);
-
- requests.push_back(
- thread->invokeWithCallback(&TestWorker::run, callback));
+ std::make_unique<util::Thread<TestWorker>>("Test", &fs);
+ thread->actor().invoke(&TestWorker::run, [&] { if (!--completed) loop.stop(); });
threads.push_back(std::move(thread));
}
diff --git a/test/storage/default_file_source.test.cpp b/test/storage/default_file_source.test.cpp
index 03f1076559..b5686b5ffe 100644
--- a/test/storage/default_file_source.test.cpp
+++ b/test/storage/default_file_source.test.cpp
@@ -1,5 +1,7 @@
+#include <mbgl/actor/actor.hpp>
#include <mbgl/test/util.hpp>
#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/util/run_loop.hpp>
using namespace mbgl;
@@ -20,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;
@@ -32,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);
@@ -49,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();
}
});
@@ -94,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();
}
});
@@ -137,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) {
@@ -147,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();
});
});
@@ -201,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();
@@ -230,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();
@@ -259,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();
@@ -299,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();
@@ -332,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);
@@ -366,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);
@@ -401,6 +444,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);
@@ -435,6 +479,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));
@@ -469,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();
@@ -482,7 +528,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
DefaultFileSource fs(":memory:", ".");
// Translates the URL "localhost://test to http://127.0.0.1:3000/test
- fs.setResourceTransform([](Resource::Kind, std::string&& url) -> std::string {
+ Actor<ResourceTransform> transform(loop, [](Resource::Kind, const std::string&& url) -> std::string {
if (url == "localhost://test") {
return "http://127.0.0.1:3000/test";
} else {
@@ -490,19 +536,116 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
}
});
- const Resource resource { Resource::Unknown, "localhost://test" };
+ fs.setResourceTransform(transform.self());
+ const Resource resource1 { Resource::Unknown, "localhost://test" };
std::unique_ptr<AsyncRequest> req;
- req = fs.request(resource, [&](Response res) {
+ req = fs.request(resource1, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
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();
});
loop.run();
+
+ fs.setResourceTransform({});
+ const Resource resource2 { Resource::Unknown, "http://127.0.0.1:3000/test" };
+
+ req = fs.request(resource2, [&](Response res) {
+ req.reset();
+ EXPECT_EQ(nullptr, res.error);
+ 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();
+ });
+
+ 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.necessity = Resource::Necessity::Optional;
+
+ // 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.necessity = Resource::Necessity::Required;
+ 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/local_file_source.test.cpp b/test/storage/local_file_source.test.cpp
index 1b90e5bb1e..4d509e6c7d 100644
--- a/test/storage/local_file_source.test.cpp
+++ b/test/storage/local_file_source.test.cpp
@@ -3,7 +3,7 @@
#include <mbgl/util/run_loop.hpp>
#include <unistd.h>
-#include <limits.h>
+#include <climits>
#include <gtest/gtest.h>
namespace {
diff --git a/test/storage/offline.test.cpp b/test/storage/offline.test.cpp
index 0faaabc298..e7ebe5199f 100644
--- a/test/storage/offline.test.cpp
+++ b/test/storage/offline.test.cpp
@@ -52,3 +52,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 fbf515679e..d99c1f946f 100644
--- a/test/storage/offline_database.test.cpp
+++ b/test/storage/offline_database.test.cpp
@@ -650,19 +650,19 @@ TEST(OfflineDatabase, MigrateFromV2Schema) {
// 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"));
}
@@ -671,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) {
@@ -690,24 +690,52 @@ 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) {
@@ -723,13 +751,13 @@ TEST(OfflineDatabase, DowngradeSchema) {
OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+ 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" }),
+ "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" }),
+ "compressed", "accessed", "must_revalidate" }),
databaseTableColumns("test/fixtures/offline_database/migrated.db", "resources"));
}
diff --git a/test/storage/offline_download.test.cpp b/test/storage/offline_download.test.cpp
index 27e57771c8..57780eba40 100644
--- a/test/storage/offline_download.test.cpp
+++ b/test/storage/offline_download.test.cpp
@@ -191,6 +191,11 @@ TEST(OfflineDownload, Activate) {
return test.response("sprite.png");
};
+ test.fileSource.imageResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/radar.gif", resource.url);
+ return test.response("radar.gif");
+ };
+
test.fileSource.spriteJSONResponse = [&] (const Resource& resource) {
EXPECT_EQ("http://127.0.0.1:3000/sprite.json", resource.url);
return test.response("sprite.json");
@@ -219,7 +224,7 @@ TEST(OfflineDownload, Activate) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
- EXPECT_EQ(261u, status.completedResourceCount); // 256 glyphs, 1 tile, 1 style, source, sprite image, and sprite json
+ EXPECT_EQ(262u, status.completedResourceCount); // 256 glyphs, 1 tile, 1 style, source, image, sprite image, and sprite json
EXPECT_EQ(test.size, status.completedResourceSize);
download.setState(OfflineRegionDownloadState::Inactive);
@@ -299,7 +304,7 @@ TEST(OfflineDownload, GetStatusStyleComplete) {
EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
EXPECT_EQ(1u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
- EXPECT_EQ(260u, status.requiredResourceCount);
+ EXPECT_EQ(261u, status.requiredResourceCount);
EXPECT_FALSE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
@@ -325,7 +330,7 @@ TEST(OfflineDownload, GetStatusStyleAndSourceComplete) {
EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
EXPECT_EQ(2u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
- EXPECT_EQ(261u, status.requiredResourceCount);
+ EXPECT_EQ(262u, status.requiredResourceCount);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
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 1c15fe6503..5a27aa98a5 100644
--- a/test/storage/resource.test.cpp
+++ b/test/storage/resource.test.cpp
@@ -117,6 +117,13 @@ TEST(Resource, SpriteImage) {
EXPECT_EQ("http://example.com/sprite@2x.png", resource.url);
}
+TEST(Resource, Image) {
+ using namespace mbgl;
+ Resource resource = Resource::image("http://example.com/sprite.jpg");
+ EXPECT_EQ(Resource::Kind::Image, resource.kind);
+ EXPECT_EQ("http://example.com/sprite.jpg", resource.url);
+}
+
TEST(Resource, SpriteJSON) {
using namespace mbgl;
Resource resource = Resource::spriteJSON("http://example.com/sprite", 2.0);
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 08637d40cb..1eff94d939 100644
--- a/test/style/conversion/function.test.cpp
+++ b/test/style/conversion/function.test.cpp
@@ -19,26 +19,26 @@ TEST(StyleConversion, Function) {
return convert<CameraFunction<float>, JSValue>(doc, error);
};
- auto fn1 = parseFunction("{\"stops\":[]}");
+ auto fn1 = parseFunction(R"({"stops":[]})");
ASSERT_FALSE(fn1);
ASSERT_EQ("function must have at least one stop", error.message);
- auto fn2 = parseFunction("{\"stops\":[1]}");
+ auto fn2 = parseFunction(R"({"stops":[1]})");
ASSERT_FALSE(fn2);
ASSERT_EQ("function stop must be an array", error.message);
- auto fn3 = parseFunction("{\"stops\":[[]]}");
+ auto fn3 = parseFunction(R"({"stops":[[]]})");
ASSERT_FALSE(fn3);
ASSERT_EQ("function stop must have two elements", error.message);
- auto fn4 = parseFunction("{\"stops\":[[-1,-1]]}");
+ auto fn4 = parseFunction(R"({"stops":[[-1,-1]]})");
ASSERT_TRUE(bool(fn4));
- auto fn5 = parseFunction("{\"stops\":[[0,1,2]]}");
+ auto fn5 = parseFunction(R"({"stops":[[0,1,2]]})");
ASSERT_FALSE(fn5);
ASSERT_EQ("function stop must have two elements", error.message);
- auto fn6 = parseFunction("{\"stops\":[[0,\"x\"]]}");
+ auto fn6 = parseFunction(R"({"stops":[[0,"x"]]})");
ASSERT_FALSE(fn6);
ASSERT_EQ("value must be a number", error.message);
@@ -50,7 +50,7 @@ TEST(StyleConversion, Function) {
ASSERT_FALSE(fn8);
ASSERT_EQ("function must be an object", error.message);
- auto fn9 = parseFunction("{\"stops\":[[0,0]],\"base\":false}");
+ auto fn9 = parseFunction(R"({"stops":[[0,0]],"base":false})");
ASSERT_FALSE(fn9);
ASSERT_EQ("function base must be a number", error.message);
}
diff --git a/test/style/conversion/layer.test.cpp b/test/style/conversion/layer.test.cpp
index ae8d4058ab..d51d7d33e2 100644
--- a/test/style/conversion/layer.test.cpp
+++ b/test/style/conversion/layer.test.cpp
@@ -27,21 +27,11 @@ TEST(StyleConversion, LayerTransition) {
"duration": 400,
"delay": 500
}
- },
- "paint.class": {
- "background-color-transition": {
- "duration": 100
- }
}
})JSON");
- ASSERT_EQ(400ms, *layer->as<BackgroundLayer>()->impl->cascading
- .get<BackgroundColor>().getTransition({}).duration);
- ASSERT_EQ(500ms, *layer->as<BackgroundLayer>()->impl->cascading
- .get<BackgroundColor>().getTransition({}).delay);
-
- ASSERT_EQ(100ms, *layer->as<BackgroundLayer>()->impl->cascading
- .get<BackgroundColor>().getTransition({"class"}).duration);
- ASSERT_FALSE(bool(layer->as<BackgroundLayer>()->impl->cascading
- .get<BackgroundColor>().getTransition({"class"}).delay));
+ ASSERT_EQ(400ms, *layer->as<BackgroundLayer>()->impl().paint
+ .get<BackgroundColor>().options.duration);
+ ASSERT_EQ(500ms, *layer->as<BackgroundLayer>()->impl().paint
+ .get<BackgroundColor>().options.delay);
}
diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp
index a2185906d6..28e22b3550 100644
--- a/test/style/conversion/light.test.cpp
+++ b/test/style/conversion/light.test.cpp
@@ -30,7 +30,7 @@ TEST(StyleConversion, Light) {
}
{
- auto light = parseLight("{\"color\":{\"stops\":[[14,\"blue\"],[16,\"red\"]]},\"intensity\":0.3,\"position\":[3,90,90]}");
+ auto light = parseLight(R"({"color":{"stops":[[14,"blue"],[16,"red"]]},"intensity":0.3,"position":[3,90,90]})");
ASSERT_TRUE((bool) light);
ASSERT_TRUE(light->getAnchor().isUndefined());
@@ -54,7 +54,7 @@ TEST(StyleConversion, Light) {
}
{
- auto light = parseLight("{\"color\":\"blue\",\"intensity\":0.3,\"color-transition\":{\"duration\":1000}}");
+ auto light = parseLight(R"({"color":"blue","intensity":0.3,"color-transition":{"duration":1000}})");
ASSERT_TRUE((bool) light);
ASSERT_FALSE(light->getColor().isUndefined());
@@ -65,35 +65,35 @@ TEST(StyleConversion, Light) {
}
{
- auto light = parseLight("{\"intensity\":false}");
+ auto light = parseLight(R"({"intensity":false})");
ASSERT_FALSE((bool) light);
ASSERT_EQ("value must be a number", error.message);
}
{
- auto light = parseLight("{\"intensity\":{\"stops\":[[15,\"red\"],[17,\"blue\"]]}}");
+ auto light = parseLight(R"({"intensity":{"stops":[[15,"red"],[17,"blue"]]}})");
ASSERT_FALSE((bool) light);
ASSERT_EQ("value must be a number", error.message);
}
{
- auto light = parseLight("{\"color\":5}");
+ auto light = parseLight(R"({"color":5})");
ASSERT_FALSE((bool) light);
ASSERT_EQ("value must be a string", error.message);
}
{
- auto light = parseLight("{\"position\":[0,5]}");
+ auto light = parseLight(R"({"position":[0,5]})");
ASSERT_FALSE((bool) light);
ASSERT_EQ("value must be an array of 3 numbers", error.message);
}
{
- auto light = parseLight("{\"anchor\":\"something\"}");
+ auto light = parseLight(R"({"anchor":"something"})");
ASSERT_FALSE((bool) light);
ASSERT_EQ("value must be a valid enumeration value", error.message);
diff --git a/test/style/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp
index 1dae20b26b..0b2940a0e0 100644
--- a/test/style/conversion/stringify.test.cpp
+++ b/test/style/conversion/stringify.test.cpp
@@ -121,10 +121,17 @@ TEST(Stringify, PropertyValue) {
}
TEST(Stringify, Layout) {
- ASSERT_EQ(stringify(SymbolLayoutProperties()), "{}");
-
- SymbolLayoutProperties layout;
- layout.unevaluated.get<SymbolAvoidEdges>() = true;
- layout.unevaluated.get<IconPadding>() = 2.0;
+ auto stringify = [] (const SymbolLayoutProperties::Unevaluated& layout) {
+ rapidjson::StringBuffer s;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(s);
+ layout.stringify(writer);
+ return std::string(s.GetString());
+ };
+
+ ASSERT_EQ(stringify(SymbolLayoutProperties::Unevaluated()), "{}");
+
+ SymbolLayoutProperties::Unevaluated layout;
+ layout.get<SymbolAvoidEdges>() = true;
+ layout.get<IconPadding>() = 2.0;
ASSERT_EQ(stringify(layout), "{\"symbol-avoid-edges\":true,\"icon-padding\":2.0}");
}
diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp
index c70792d8ef..96de125945 100644
--- a/test/style/filter.test.cpp
+++ b/test/style/filter.test.cpp
@@ -29,13 +29,13 @@ Feature feature(const PropertyMap& properties, const Geometry<double>& geometry
}
TEST(Filter, EqualsString) {
- Filter f = parse("[\"==\", \"foo\", \"bar\"]");
+ Filter f = parse(R"(["==", "foo", "bar"])");
ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }})));
ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }})));
}
TEST(Filter, EqualsNumber) {
- Filter f = parse("[\"==\", \"foo\", 0]");
+ Filter f = parse(R"(["==", "foo", 0])");
ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }})));
ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }})));
ASSERT_TRUE(f(feature({{ "foo", double(0) }})));
@@ -50,13 +50,13 @@ TEST(Filter, EqualsNumber) {
}
TEST(Filter, EqualsType) {
- Filter f = parse("[\"==\", \"$type\", \"LineString\"]");
+ Filter f = parse(R"(["==", "$type", "LineString"])");
ASSERT_FALSE(f(feature({{}}, Point<double>())));
ASSERT_TRUE(f(feature({{}}, LineString<double>())));
}
TEST(Filter, InType) {
- Filter f = parse("[\"in\", \"$type\", \"LineString\", \"Polygon\"]");
+ Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])");
ASSERT_FALSE(f(feature({{}}, Point<double>())));
ASSERT_TRUE(f(feature({{}}, LineString<double>())));
ASSERT_TRUE(f(feature({{}}, Polygon<double>())));
diff --git a/test/style/function/exponential_stops.test.cpp b/test/style/function/exponential_stops.test.cpp
new file mode 100644
index 0000000000..81438ec952
--- /dev/null
+++ b/test/style/function/exponential_stops.test.cpp
@@ -0,0 +1,20 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/style/function/exponential_stops.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(ExponentialStops, Empty) {
+ ExponentialStops<float> stops;
+ EXPECT_FALSE(bool(stops.evaluate(0)));
+}
+
+TEST(ExponentialStops, NonNumericInput) {
+ ExponentialStops<float> stops(std::map<float, float> {{0.0f, 0.0f}});
+ EXPECT_FALSE(bool(stops.evaluate(Value(NullValue()))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(false))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(std::string()))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(std::vector<Value>()))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(std::unordered_map<std::string, Value>()))));
+}
diff --git a/test/style/function/interval_stops.test.cpp b/test/style/function/interval_stops.test.cpp
new file mode 100644
index 0000000000..8a5e74b8b6
--- /dev/null
+++ b/test/style/function/interval_stops.test.cpp
@@ -0,0 +1,20 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/style/function/interval_stops.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(IntervalStops, Empty) {
+ IntervalStops<float> stops;
+ EXPECT_FALSE(bool(stops.evaluate(0)));
+}
+
+TEST(IntervalStops, NonNumericInput) {
+ IntervalStops<float> stops(std::map<float, float> {{0.0f, 0.0f}});
+ EXPECT_FALSE(bool(stops.evaluate(Value(NullValue()))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(false))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(std::string()))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(std::vector<Value>()))));
+ EXPECT_FALSE(bool(stops.evaluate(Value(std::unordered_map<std::string, Value>()))));
+}
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/paint_property.test.cpp b/test/style/properties.test.cpp
index fcca05f3bd..279fadb8c2 100644
--- a/test/style/paint_property.test.cpp
+++ b/test/style/properties.test.cpp
@@ -1,13 +1,14 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/style/paint_property.hpp>
-#include <mbgl/renderer/transitioning_property.hpp>
+#include <mbgl/style/properties.hpp>
+#include <mbgl/renderer/property_evaluator.hpp>
+#include <mbgl/renderer/data_driven_property_evaluator.hpp>
using namespace mbgl;
using namespace mbgl::style;
using namespace std::literals::chrono_literals;
-float evaluate(TransitioningProperty<PropertyValue<float>>& property, Duration delta = Duration::zero()) {
+float evaluate(Transitioning<PropertyValue<float>>& property, Duration delta = Duration::zero()) {
ZoomHistory zoomHistory;
zoomHistory.update(0, TimePoint::min() + delta);
@@ -25,7 +26,7 @@ float evaluate(TransitioningProperty<PropertyValue<float>>& property, Duration d
return property.evaluate(evaluator, parameters.now);
}
-PossiblyEvaluatedPropertyValue<float> evaluate(TransitioningProperty<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) {
+PossiblyEvaluatedPropertyValue<float> evaluate(Transitioning<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) {
ZoomHistory zoomHistory;
zoomHistory.update(0, TimePoint::min() + delta);
@@ -43,15 +44,15 @@ PossiblyEvaluatedPropertyValue<float> evaluate(TransitioningProperty<DataDrivenP
return property.evaluate(evaluator, parameters.now);
}
-TEST(TransitioningProperty, EvaluateDefaultValue) {
- TransitioningProperty<PropertyValue<float>> property;
+TEST(TransitioningPropertyValue, EvaluateDefaultValue) {
+ Transitioning<PropertyValue<float>> property;
ASSERT_EQ(0.0f, evaluate(property));
}
-TEST(TransitioningProperty, EvaluateUntransitionedConstant) {
- TransitioningProperty<PropertyValue<float>> property {
+TEST(TransitioningPropertyValue, EvaluateUntransitionedConstant) {
+ Transitioning<PropertyValue<float>> property {
PropertyValue<float>(1.0f),
- TransitioningProperty<PropertyValue<float>>(),
+ Transitioning<PropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
@@ -59,18 +60,18 @@ TEST(TransitioningProperty, EvaluateUntransitionedConstant) {
ASSERT_EQ(1.0f, evaluate(property));
}
-TEST(TransitioningProperty, EvaluateTransitionedConstantWithoutDelay) {
+TEST(TransitioningPropertyValue, EvaluateTransitionedConstantWithoutDelay) {
TransitionOptions transition;
transition.duration = { 1000ms };
- TransitioningProperty<PropertyValue<float>> t0 {
+ Transitioning<PropertyValue<float>> t0 {
PropertyValue<float>(0.0f),
- TransitioningProperty<PropertyValue<float>>(),
+ Transitioning<PropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
- TransitioningProperty<PropertyValue<float>> t1 {
+ Transitioning<PropertyValue<float>> t1 {
PropertyValue<float>(1.0f),
t0,
transition,
@@ -82,19 +83,19 @@ TEST(TransitioningProperty, EvaluateTransitionedConstantWithoutDelay) {
ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 1500ms));
}
-TEST(TransitioningProperty, EvaluateTransitionedConstantWithDelay) {
+TEST(TransitioningPropertyValue, EvaluateTransitionedConstantWithDelay) {
TransitionOptions transition;
transition.delay = { 1000ms };
transition.duration = { 1000ms };
- TransitioningProperty<PropertyValue<float>> t0 {
+ Transitioning<PropertyValue<float>> t0 {
PropertyValue<float>(0.0f),
- TransitioningProperty<PropertyValue<float>>(),
+ Transitioning<PropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
- TransitioningProperty<PropertyValue<float>> t1 {
+ Transitioning<PropertyValue<float>> t1 {
PropertyValue<float>(1.0f),
t0,
transition,
@@ -108,14 +109,14 @@ TEST(TransitioningProperty, EvaluateTransitionedConstantWithDelay) {
ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 2500ms));
}
-TEST(TransitioningProperty, EvaluateDataDrivenValue) {
+TEST(TransitioningDataDrivenPropertyValue, Evaluate) {
TransitionOptions transition;
transition.delay = { 1000ms };
transition.duration = { 1000ms };
- TransitioningProperty<DataDrivenPropertyValue<float>> t0 {
+ Transitioning<DataDrivenPropertyValue<float>> t0 {
DataDrivenPropertyValue<float>(0.0f),
- TransitioningProperty<DataDrivenPropertyValue<float>>(),
+ Transitioning<DataDrivenPropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
@@ -125,7 +126,7 @@ TEST(TransitioningProperty, EvaluateDataDrivenValue) {
IdentityStops<float>()
};
- TransitioningProperty<DataDrivenPropertyValue<float>> t1 {
+ Transitioning<DataDrivenPropertyValue<float>> t1 {
DataDrivenPropertyValue<float>(sourceFunction),
t0,
transition,
diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp
index c60a473589..919260ffe9 100644
--- a/test/style/source.test.cpp
+++ b/test/style/source.test.cpp
@@ -3,10 +3,14 @@
#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/layers/raster_layer.cpp>
+#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/renderer/sources/render_raster_source.hpp>
#include <mbgl/renderer/sources/render_vector_source.hpp>
@@ -16,6 +20,9 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/io.hpp>
+#include <mbgl/util/premultiply.hpp>
+#include <mbgl/util/image.hpp>
+
#include <mbgl/util/tileset.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/logging.hpp>
@@ -23,12 +30,10 @@
#include <mbgl/util/range.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/annotation/annotation_source.hpp>
-
-#include <mapbox/geojsonvt.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/text/glyph_manager.hpp>
#include <cstdint>
@@ -43,8 +48,10 @@ public:
Transform transform;
TransformState transformState;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager { 1.0 };
- style::Style style { threadPool, fileSource, 1.0 };
+ Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
TileParameters tileParameters {
1.0,
@@ -54,7 +61,9 @@ public:
fileSource,
MapMode::Continuous,
annotationManager,
- style
+ imageManager,
+ glyphManager,
+ 0
};
SourceTest() {
@@ -95,8 +104,8 @@ TEST(Source, LoadingFail) {
};
VectorSource source("source", "url");
- source.baseImpl->setObserver(&test.styleObserver);
- source.baseImpl->loadDescription(test.fileSource);
+ source.setObserver(&test.styleObserver);
+ source.loadDescription(test.fileSource);
test.run();
}
@@ -118,8 +127,8 @@ TEST(Source, LoadingCorrupt) {
};
VectorSource source("source", "url");
- source.baseImpl->setObserver(&test.styleObserver);
- source.baseImpl->loadDescription(test.fileSource);
+ source.setObserver(&test.styleObserver);
+ source.loadDescription(test.fileSource);
test.run();
}
@@ -133,14 +142,17 @@ TEST(Source, RasterTileEmpty) {
return response;
};
+ RasterLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
Tileset tileset;
tileset.tiles = { "tiles" };
RasterSource source("source", tileset, 512);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) {
- EXPECT_EQ("source", source_.baseImpl.id);
+ EXPECT_EQ("source", source_.baseImpl->id);
test.end();
};
@@ -148,9 +160,13 @@ TEST(Source, RasterTileEmpty) {
FAIL() << "Should never be called";
};
- RenderRasterSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -164,14 +180,19 @@ TEST(Source, VectorTileEmpty) {
return response;
};
+ LineLayer layer("id", "source");
+ layer.setSourceLayer("water");
+
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
Tileset tileset;
tileset.tiles = { "tiles" };
VectorSource source("source", tileset);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileChanged = [&] (RenderSource& source_, const OverscaledTileID&) {
- EXPECT_EQ("source", source_.baseImpl.id);
+ EXPECT_EQ("source", source_.baseImpl->id);
test.end();
};
@@ -179,9 +200,13 @@ TEST(Source, VectorTileEmpty) {
FAIL() << "Should never be called";
};
- RenderVectorSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -197,22 +222,29 @@ TEST(Source, RasterTileFail) {
return response;
};
+ RasterLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
Tileset tileset;
tileset.tiles = { "tiles" };
RasterSource source("source", tileset, 512);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(SourceType::Raster, source_.baseImpl.type);
+ EXPECT_EQ(SourceType::Raster, source_.baseImpl->type);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_EQ("Failed by the test case", util::toString(error));
test.end();
};
- RenderRasterSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -228,22 +260,31 @@ TEST(Source, VectorTileFail) {
return response;
};
+ LineLayer layer("id", "source");
+ layer.setSourceLayer("water");
+
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
Tileset tileset;
tileset.tiles = { "tiles" };
VectorSource source("source", tileset);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(SourceType::Vector, source_.baseImpl.type);
+ EXPECT_EQ(SourceType::Vector, source_.baseImpl->type);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_EQ("Failed by the test case", util::toString(error));
test.end();
};
- RenderVectorSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -257,23 +298,30 @@ TEST(Source, RasterTileCorrupt) {
return response;
};
+ RasterLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
Tileset tileset;
tileset.tiles = { "tiles" };
RasterSource source("source", tileset, 512);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(source_.baseImpl.type, SourceType::Raster);
+ EXPECT_EQ(source_.baseImpl->type, SourceType::Raster);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_TRUE(bool(error));
// Not asserting on platform-specific error text.
test.end();
};
- RenderRasterSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -287,27 +335,31 @@ TEST(Source, VectorTileCorrupt) {
return response;
};
- // Need to have at least one layer that uses the source.
- auto layer = std::make_unique<LineLayer>("id", "source");
- layer->setSourceLayer("water");
- test.style.addLayer(std::move(layer));
+ LineLayer layer("id", "source");
+ layer.setSourceLayer("water");
+
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
Tileset tileset;
tileset.tiles = { "tiles" };
VectorSource source("source", tileset);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileError = [&] (RenderSource& source_, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(source_.baseImpl.type, SourceType::Vector);
+ EXPECT_EQ(source_.baseImpl->type, SourceType::Vector);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_EQ(util::toString(error), "unknown pbf field type exception");
test.end();
};
- RenderVectorSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -320,11 +372,14 @@ TEST(Source, RasterTileCancel) {
return optional<Response>();
};
+ RasterLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
Tileset tileset;
tileset.tiles = { "tiles" };
RasterSource source("source", tileset, 512);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) {
FAIL() << "Should never be called";
@@ -334,9 +389,13 @@ TEST(Source, RasterTileCancel) {
FAIL() << "Should never be called";
};
- RenderRasterSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -349,11 +408,16 @@ TEST(Source, VectorTileCancel) {
return optional<Response>();
};
+ LineLayer layer("id", "source");
+ layer.setSourceLayer("water");
+
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
Tileset tileset;
tileset.tiles = { "tiles" };
VectorSource source("source", tileset);
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
test.renderSourceObserver.tileChanged = [&] (RenderSource&, const OverscaledTileID&) {
FAIL() << "Should never be called";
@@ -363,9 +427,13 @@ TEST(Source, VectorTileCancel) {
FAIL() << "Should never be called";
};
- RenderVectorSource renderSource(*source.impl);
- renderSource.setObserver(&test.renderSourceObserver);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->setObserver(&test.renderSourceObserver);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -373,6 +441,9 @@ TEST(Source, VectorTileCancel) {
TEST(Source, RasterTileAttribution) {
SourceTest test;
+ RasterLayer layer("id", "source");
+ std::vector<Immutable<Layer::Impl>> layers {{ layer.baseImpl }};
+
std::string mapboxOSM = ("<a href='https://www.mapbox.com/about/maps/' target='_blank'>&copy; Mapbox</a> "
"<a href='http://www.openstreetmap.org/about/' target='_blank'>©️ OpenStreetMap</a>");
@@ -398,11 +469,15 @@ TEST(Source, RasterTileAttribution) {
};
RasterSource source("source", "url", 512);
- source.baseImpl->setObserver(&test.styleObserver);
- source.baseImpl->loadDescription(test.fileSource);
+ source.setObserver(&test.styleObserver);
+ source.loadDescription(test.fileSource);
- RenderRasterSource renderSource(*source.impl);
- renderSource.updateTiles(test.tileParameters);
+ auto renderSource = RenderSource::create(source.baseImpl);
+ renderSource->update(source.baseImpl,
+ layers,
+ true,
+ true,
+ test.tileParameters);
test.run();
}
@@ -413,7 +488,7 @@ TEST(Source, GeoJSonSourceUrlUpdate) {
test.fileSource.sourceResponse = [&] (const Resource& resource) {
EXPECT_EQ("url", resource.url);
Response response;
- response.data = std::make_unique<std::string>("{\"geometry\": {\"type\": \"Point\", \"coordinates\": [1.1, 1.1]}, \"type\": \"Feature\", \"properties\": {}}");
+ response.data = std::make_unique<std::string>(R"({"geometry": {"type": "Point", "coordinates": [1.1, 1.1]}, "type": "Feature", "properties": {}})");
return response;
};
@@ -423,10 +498,10 @@ TEST(Source, GeoJSonSourceUrlUpdate) {
};
GeoJSONSource source("source");
- source.baseImpl->setObserver(&test.styleObserver);
+ source.setObserver(&test.styleObserver);
// Load initial, so the source state will be loaded=true
- source.baseImpl->loadDescription(test.fileSource);
+ source.loadDescription(test.fileSource);
// Schedule an update
test.loop.invoke([&] () {
@@ -436,3 +511,39 @@ TEST(Source, GeoJSonSourceUrlUpdate) {
test.run();
}
+
+TEST(Source, ImageSourceImageUpdate) {
+ SourceTest test;
+
+ test.fileSource.response = [&] (const Resource& resource) {
+ EXPECT_EQ("http://url", resource.url);
+ Response response;
+ response.data = std::make_unique<std::string>(util::read_file("test/fixtures/image/no_profile.png"));
+ return response;
+ };
+ test.styleObserver.sourceChanged = [&] (Source&) {
+ // Should be called (test will hang if it doesn't)
+ test.end();
+ };
+ std::array<LatLng, 4> coords;
+
+ ImageSource source("source", coords);
+ source.setURL("http://url");
+ source.setObserver(&test.styleObserver);
+
+ // Load initial, so the source state will be loaded=true
+ source.loadDescription(test.fileSource);
+ PremultipliedImage rgba({ 1, 1 });
+ rgba.data[0] = 255;
+ rgba.data[1] = 254;
+ rgba.data[2] = 253;
+ rgba.data[3] = 0;
+
+ // Schedule an update
+ test.loop.invoke([&] () {
+ // Update the url
+ source.setImage(std::move(rgba));
+ });
+
+ test.run();
+}
diff --git a/test/style/style.test.cpp b/test/style/style.test.cpp
index 841c7b291b..9bdab37ac6 100644
--- a/test/style/style.test.cpp
+++ b/test/style/style.test.cpp
@@ -2,7 +2,7 @@
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/test/fixture_log_observer.hpp>
-#include <mbgl/style/style.hpp>
+#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/source_impl.hpp>
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/style/layer.hpp>
@@ -21,34 +21,34 @@ TEST(Style, Properties) {
ThreadPool threadPool{ 1 };
StubFileSource fileSource;
- Style style { threadPool, fileSource, 1.0 };
+ Style::Impl style { threadPool, fileSource, 1.0 };
- style.setJSON(R"STYLE({"name": "Test"})STYLE");
+ style.loadJSON(R"STYLE({"name": "Test"})STYLE");
ASSERT_EQ("Test", style.getName());
- style.setJSON(R"STYLE({"center": [10, 20]})STYLE");
+ 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.setJSON(R"STYLE({"bearing": 24})STYLE");
+ 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.setJSON(R"STYLE({"zoom": 13.3})STYLE");
+ 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.setJSON(R"STYLE({"pitch": 60})STYLE");
+ style.loadJSON(R"STYLE({"pitch": 60})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ(60, style.getDefaultPitch());
+ ASSERT_EQ(60, *style.getDefaultCamera().pitch);
- style.setJSON(R"STYLE({"name": 23, "center": {}, "bearing": "north", "zoom": null, "pitch": "wide"})STYLE");
+ 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) {
@@ -56,9 +56,9 @@ TEST(Style, DuplicateSource) {
ThreadPool threadPool{ 1 };
StubFileSource fileSource;
- Style style { threadPool, fileSource, 1.0 };
+ Style::Impl style { threadPool, fileSource, 1.0 };
- style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"));
+ style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"));
style.addSource(std::make_unique<VectorSource>("sourceId", "mapbox://mapbox.mapbox-terrain-v2"));
@@ -78,9 +78,9 @@ TEST(Style, RemoveSourceInUse) {
ThreadPool threadPool{ 1 };
StubFileSource fileSource;
- Style style { threadPool, fileSource, 1.0 };
+ Style::Impl style { threadPool, fileSource, 1.0 };
- style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"));
+ style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"));
style.addSource(std::make_unique<VectorSource>("sourceId", "mapbox://mapbox.mapbox-terrain-v2"));
style.addLayer(std::make_unique<LineLayer>("layerId", "sourceId"));
diff --git a/test/style/style_image.test.cpp b/test/style/style_image.test.cpp
index 319120df83..e49bf37582 100644
--- a/test/style/style_image.test.cpp
+++ b/test/style/style_image.test.cpp
@@ -8,7 +8,7 @@ using namespace mbgl;
TEST(StyleImage, ZeroWidth) {
try {
- style::Image(PremultipliedImage({ 0, 16 }), 2.0);
+ style::Image("test", PremultipliedImage({ 0, 16 }), 2.0);
FAIL() << "Expected exception";
} catch (util::SpriteImageException& ex) {
EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what());
@@ -17,7 +17,7 @@ TEST(StyleImage, ZeroWidth) {
TEST(StyleImage, ZeroHeight) {
try {
- style::Image(PremultipliedImage({ 16, 0 }), 2.0);
+ style::Image("test", PremultipliedImage({ 16, 0 }), 2.0);
FAIL() << "Expected exception";
} catch (util::SpriteImageException& ex) {
EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what());
@@ -26,7 +26,7 @@ TEST(StyleImage, ZeroHeight) {
TEST(StyleImage, ZeroRatio) {
try {
- style::Image(PremultipliedImage({ 16, 16 }), 0.0);
+ style::Image("test", PremultipliedImage({ 16, 16 }), 0.0);
FAIL() << "Expected exception";
} catch (util::SpriteImageException& ex) {
EXPECT_STREQ("Sprite pixelRatio may not be <= 0", ex.what());
@@ -34,19 +34,15 @@ TEST(StyleImage, ZeroRatio) {
}
TEST(StyleImage, Retina) {
- style::Image image(PremultipliedImage({ 32, 24 }), 2.0);
- EXPECT_EQ(16, image.getWidth());
- EXPECT_EQ(32u, image.image.size.width);
- EXPECT_EQ(12, image.getHeight());
- EXPECT_EQ(24u, image.image.size.height);
- EXPECT_EQ(2, image.pixelRatio);
+ style::Image image("test", PremultipliedImage({ 32, 24 }), 2.0);
+ EXPECT_EQ(32u, image.getImage().size.width);
+ EXPECT_EQ(24u, image.getImage().size.height);
+ EXPECT_EQ(2, image.getPixelRatio());
}
TEST(StyleImage, FractionalRatio) {
- style::Image image(PremultipliedImage({ 20, 12 }), 1.5);
- EXPECT_EQ(float(20.0 / 1.5), image.getWidth());
- EXPECT_EQ(20u, image.image.size.width);
- EXPECT_EQ(float(12.0 / 1.5), image.getHeight());
- EXPECT_EQ(12u, image.image.size.height);
- EXPECT_EQ(1.5, image.pixelRatio);
+ style::Image image("test", PremultipliedImage({ 20, 12 }), 1.5);
+ EXPECT_EQ(20u, image.getImage().size.width);
+ EXPECT_EQ(12u, image.getImage().size.height);
+ EXPECT_EQ(1.5, image.getPixelRatio());
}
diff --git a/test/style/style_layer.test.cpp b/test/style/style_layer.test.cpp
index 657dc24a70..77acca2868 100644
--- a/test/style/style_layer.test.cpp
+++ b/test/style/style_layer.test.cpp
@@ -1,7 +1,7 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/stub_layer_observer.hpp>
#include <mbgl/test/stub_file_source.hpp>
-#include <mbgl/style/style.hpp>
+#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
@@ -28,15 +28,6 @@ using namespace mbgl::style;
namespace {
-template <class T, class... Params> void testClone(Params... params) {
- auto layer = std::make_unique<T>(std::forward<Params>(params)...);
- auto clone = layer->baseImpl->clone();
- EXPECT_NE(layer.get(), clone.get());
- EXPECT_TRUE(reinterpret_cast<typename T::Impl*>(clone->baseImpl.get()));
- layer->impl->id = "test";
- EXPECT_EQ("test", layer->baseImpl->clone()->getID());
-}
-
const auto color = Color { 1, 0, 0, 1 };
const auto opacity = 1.0f;
const auto radius = 1.0f;
@@ -61,16 +52,6 @@ const auto duration = 1.0f;
} // namespace
-TEST(Layer, Clone) {
- testClone<BackgroundLayer>("background");
- testClone<CircleLayer>("circle", "source");
- testClone<CustomLayer>("custom", [](void*){}, [](void*, const CustomLayerRenderParameters&){}, [](void*){}, nullptr),
- testClone<FillLayer>("fill", "source");
- testClone<LineLayer>("line", "source");
- testClone<RasterLayer>("raster", "source");
- testClone<SymbolLayer>("symbol", "source");
-}
-
TEST(Layer, BackgroundProperties) {
auto layer = std::make_unique<BackgroundLayer>("background");
EXPECT_TRUE(layer->is<BackgroundLayer>());
@@ -222,11 +203,11 @@ TEST(Layer, RasterProperties) {
TEST(Layer, Observer) {
auto layer = std::make_unique<LineLayer>("line", "source");
StubLayerObserver observer;
- layer->baseImpl->setObserver(&observer);
+ layer->setObserver(&observer);
// Notifies observer on filter change.
bool filterChanged = false;
- observer.layerFilterChanged = [&] (Layer& layer_) {
+ observer.layerChanged = [&] (Layer& layer_) {
EXPECT_EQ(layer.get(), &layer_);
filterChanged = true;
};
@@ -235,7 +216,7 @@ TEST(Layer, Observer) {
// Notifies observer on visibility change.
bool visibilityChanged = false;
- observer.layerVisibilityChanged = [&] (Layer& layer_) {
+ observer.layerChanged = [&] (Layer& layer_) {
EXPECT_EQ(layer.get(), &layer_);
visibilityChanged = true;
};
@@ -244,7 +225,7 @@ TEST(Layer, Observer) {
// Notifies observer on paint property change.
bool paintPropertyChanged = false;
- observer.layerPaintPropertyChanged = [&] (Layer& layer_) {
+ observer.layerChanged = [&] (Layer& layer_) {
EXPECT_EQ(layer.get(), &layer_);
paintPropertyChanged = true;
};
@@ -253,7 +234,7 @@ TEST(Layer, Observer) {
// Notifies observer on layout property change.
bool layoutPropertyChanged = false;
- observer.layerLayoutPropertyChanged = [&] (Layer& layer_, const char *) {
+ observer.layerChanged = [&] (Layer& layer_) {
EXPECT_EQ(layer.get(), &layer_);
layoutPropertyChanged = true;
};
@@ -262,16 +243,28 @@ TEST(Layer, Observer) {
// Does not notify observer on no-op visibility change.
visibilityChanged = false;
+ observer.layerChanged = [&] (Layer& layer_) {
+ EXPECT_EQ(layer.get(), &layer_);
+ visibilityChanged = true;
+ };
layer->setVisibility(VisibilityType::None);
EXPECT_FALSE(visibilityChanged);
// Does not notify observer on no-op paint property change.
paintPropertyChanged = false;
+ observer.layerChanged = [&] (Layer& layer_) {
+ EXPECT_EQ(layer.get(), &layer_);
+ paintPropertyChanged = true;
+ };
layer->setLineColor(color);
EXPECT_FALSE(paintPropertyChanged);
// Does not notify observer on no-op layout property change.
layoutPropertyChanged = false;
+ observer.layerChanged = [&] (Layer& layer_) {
+ EXPECT_EQ(layer.get(), &layer_);
+ layoutPropertyChanged = true;
+ };
layer->setLineCap(lineCap);
EXPECT_FALSE(layoutPropertyChanged);
}
@@ -282,8 +275,8 @@ TEST(Layer, DuplicateLayer) {
// Setup style
ThreadPool threadPool{ 1 };
StubFileSource fileSource;
- Style style { threadPool, fileSource, 1.0 };
- style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"));
+ Style::Impl style { threadPool, fileSource, 1.0 };
+ style.loadJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"));
// Add initial layer
style.addLayer(std::make_unique<LineLayer>("line", "unusedsource"));
diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp
index e3c1da582f..5fa81b47e9 100644
--- a/test/style/style_parser.test.cpp
+++ b/test/style/style_parser.test.cpp
@@ -16,8 +16,8 @@
using namespace mbgl;
-typedef std::pair<uint32_t, std::string> Message;
-typedef std::vector<Message> Messages;
+using Message = std::pair<uint32_t, std::string>;
+using Messages = std::vector<Message>;
class StyleParserTest : public ::testing::TestWithParam<std::string> {};
diff --git a/test/text/glyph_atlas.test.cpp b/test/text/glyph_loader.test.cpp
index 5aff1ee441..be197ebb46 100644
--- a/test/text/glyph_atlas.test.cpp
+++ b/test/text/glyph_loader.test.cpp
@@ -1,8 +1,7 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/stub_file_source.hpp>
-#include <mbgl/test/stub_style_observer.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
+#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/io.hpp>
@@ -10,30 +9,44 @@
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(GlyphPositionMap positions) override {
- if (glyphsAvailable) glyphsAvailable(std::move(positions));
+ void onGlyphsAvailable(GlyphMap glyphs) override {
+ if (glyphsAvailable) glyphsAvailable(std::move(glyphs));
}
- std::function<void (GlyphPositionMap)> glyphsAvailable;
+ std::function<void (GlyphMap)> glyphsAvailable;
};
-class GlyphAtlasTest {
+class GlyphManagerTest {
public:
util::RunLoop loop;
StubFileSource fileSource;
- StubStyleObserver observer;
+ StubGlyphManagerObserver observer;
StubGlyphRequestor requestor;
- GlyphAtlas glyphAtlas{ { 32, 32 }, fileSource };
+ GlyphManager glyphManager { fileSource };
void run(const std::string& url, GlyphDependencies dependencies) {
// Squelch logging.
Log::setObserver(std::make_unique<Log::NullObserver>());
- glyphAtlas.setURL(url);
- glyphAtlas.setObserver(&observer);
- glyphAtlas.getGlyphs(requestor, std::move(dependencies));
+ glyphManager.setURL(url);
+ glyphManager.setObserver(&observer);
+ glyphManager.getGlyphs(requestor, std::move(dependencies));
loop.run();
}
@@ -43,8 +56,8 @@ public:
}
};
-TEST(GlyphAtlas, LoadingSuccess) {
- GlyphAtlasTest test;
+TEST(GlyphManager, LoadingSuccess) {
+ GlyphManagerTest test;
test.fileSource.glyphsResponse = [&] (const Resource& resource) {
EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
@@ -63,8 +76,8 @@ TEST(GlyphAtlas, LoadingSuccess) {
ASSERT_EQ(range, GlyphRange(0, 255));
};
- test.requestor.glyphsAvailable = [&] (GlyphPositionMap positions) {
- const auto& testPositions = positions.at({{"Test Stack"}});
+ test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
+ const auto& testPositions = glyphs.at({{"Test Stack"}});
ASSERT_EQ(testPositions.size(), 3u);
ASSERT_EQ(testPositions.count(u'a'), 1u);
@@ -82,8 +95,8 @@ TEST(GlyphAtlas, LoadingSuccess) {
});
}
-TEST(GlyphAtlas, LoadingFail) {
- GlyphAtlasTest test;
+TEST(GlyphManager, LoadingFail) {
+ GlyphManagerTest test;
test.fileSource.glyphsResponse = [&] (const Resource&) {
Response response;
@@ -103,7 +116,7 @@ TEST(GlyphAtlas, LoadingFail) {
test.end();
};
- test.requestor.glyphsAvailable = [&] (GlyphPositionMap) {
+ test.requestor.glyphsAvailable = [&] (GlyphMap) {
FAIL();
test.end();
};
@@ -115,8 +128,8 @@ TEST(GlyphAtlas, LoadingFail) {
});
}
-TEST(GlyphAtlas, LoadingCorrupted) {
- GlyphAtlasTest test;
+TEST(GlyphManager, LoadingCorrupted) {
+ GlyphManagerTest test;
test.fileSource.glyphsResponse = [&] (const Resource&) {
Response response;
@@ -134,7 +147,7 @@ TEST(GlyphAtlas, LoadingCorrupted) {
test.end();
};
- test.requestor.glyphsAvailable = [&] (GlyphPositionMap) {
+ test.requestor.glyphsAvailable = [&] (GlyphMap) {
FAIL();
test.end();
};
@@ -146,8 +159,8 @@ TEST(GlyphAtlas, LoadingCorrupted) {
});
}
-TEST(GlyphAtlas, LoadingCancel) {
- GlyphAtlasTest test;
+TEST(GlyphManager, LoadingCancel) {
+ GlyphManagerTest test;
test.fileSource.glyphsResponse = [&] (const Resource&) {
test.end();
@@ -165,8 +178,8 @@ TEST(GlyphAtlas, LoadingCancel) {
});
}
-TEST(GlyphAtlas, LoadingInvalid) {
- GlyphAtlasTest test;
+TEST(GlyphManager, LoadingInvalid) {
+ GlyphManagerTest test;
test.fileSource.glyphsResponse = [&] (const Resource& resource) {
EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
@@ -185,8 +198,8 @@ TEST(GlyphAtlas, LoadingInvalid) {
ASSERT_EQ(range, GlyphRange(0, 255));
};
- test.requestor.glyphsAvailable = [&] (GlyphPositionMap positions) {
- const auto& testPositions = positions.at({{"Test Stack"}});
+ test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
+ const auto& testPositions = glyphs.at({{"Test Stack"}});
ASSERT_EQ(testPositions.size(), 2u);
ASSERT_FALSE(bool(testPositions.at(u'A')));
diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp
index 83fd249535..8eedd9bd2e 100644
--- a/test/text/quads.test.cpp
+++ b/test/text/quads.test.cpp
@@ -1,5 +1,5 @@
#include <mbgl/geometry/anchor.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/style/image_impl.hpp>
#include <mbgl/test/util.hpp>
#include <mbgl/text/quads.hpp>
#include <mbgl/text/shaping.hpp>
@@ -12,48 +12,37 @@ using namespace mbgl::style;
TEST(getIconQuads, normal) {
SymbolLayoutProperties::Evaluated layout;
Anchor anchor(2.0, 3.0, 0.0, 0.5f, 0);
- SpriteAtlasElement image = {
- Rect<uint16_t>( 0, 0, 15, 11 ),
- style::Image(PremultipliedImage({1,1}), 1.0),
- { 0, 0 },
- 1.0f
+ ImagePosition image = {
+ 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);
- ASSERT_TRUE(shapedIcon);
+ 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);
-
- ASSERT_EQ(quad.anchorPoint.x, 2);
- ASSERT_EQ(quad.anchorPoint.y, 3);
- ASSERT_EQ(quad.tl.x, -8);
- ASSERT_EQ(quad.tl.y, -6);
- ASSERT_EQ(quad.tr.x, 7);
- ASSERT_EQ(quad.tr.y, -6);
- ASSERT_EQ(quad.bl.x, -8);
- ASSERT_EQ(quad.bl.y, 5);
- ASSERT_EQ(quad.br.x, 7);
- ASSERT_EQ(quad.br.y, 5);
- ASSERT_EQ(quad.anchorAngle, 0.0f);
- ASSERT_EQ(quad.glyphAngle, 0.0f);
- ASSERT_EQ(quad.minScale, 0.5f);
+ getIconQuad(shapedIcon, layout, 16.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -14);
+ EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tr.x, 1);
+ EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.bl.x, -14);
+ EXPECT_EQ(quad.bl.y, 1);
+ EXPECT_EQ(quad.br.x, 1);
+ EXPECT_EQ(quad.br.y, 1);
}
TEST(getIconQuads, style) {
Anchor anchor(0.0, 0.0, 0.0, 0.5f, 0);
- SpriteAtlasElement image = {
- Rect<uint16_t>( 0, 0, 20, 20 ),
- style::Image(PremultipliedImage({1,1}), 1.0),
- { 0, 0 },
- 1.0f
+ ImagePosition image = {
+ 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);
- ASSERT_TRUE(shapedIcon);
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
Shaping shapedText;
@@ -61,27 +50,22 @@ 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);
-
- ASSERT_EQ(quad.anchorPoint.x, 0);
- ASSERT_EQ(quad.anchorPoint.y, 0);
- ASSERT_EQ(quad.tl.x, -11);
- ASSERT_EQ(quad.tl.y, -11);
- ASSERT_EQ(quad.tr.x, 9);
- ASSERT_EQ(quad.tr.y, -11);
- ASSERT_EQ(quad.bl.x, -11);
- ASSERT_EQ(quad.bl.y, 9);
- ASSERT_EQ(quad.br.x, 9);
- ASSERT_EQ(quad.br.y, 9);
- ASSERT_EQ(quad.anchorAngle, 0.0f);
- ASSERT_EQ(quad.glyphAngle, 0.0f);
- ASSERT_EQ(quad.minScale, 0.5f);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -19.5);
+ EXPECT_EQ(quad.tl.y, -19.5);
+ EXPECT_EQ(quad.tr.x, 0.5);
+ EXPECT_EQ(quad.tr.y, -19.5);
+ EXPECT_EQ(quad.bl.x, -19.5);
+ EXPECT_EQ(quad.bl.y, 0.5);
+ EXPECT_EQ(quad.br.x, 0.5);
+ EXPECT_EQ(quad.br.y, 0.5);
}
// width
@@ -90,16 +74,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -60);
- ASSERT_EQ(quad.tl.y, 0);
- ASSERT_EQ(quad.tr.x, 20);
- ASSERT_EQ(quad.tr.y, 0);
- ASSERT_EQ(quad.bl.x, -60);
- ASSERT_EQ(quad.bl.y, 20);
- ASSERT_EQ(quad.br.x, 20);
- ASSERT_EQ(quad.br.y, 20);
+ getIconQuad(shapedIcon, layout, 24.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -60);
+ EXPECT_EQ(quad.tl.y, 0);
+ EXPECT_EQ(quad.tr.x, 20);
+ EXPECT_EQ(quad.tr.y, 0);
+ EXPECT_EQ(quad.bl.x, -60);
+ EXPECT_EQ(quad.bl.y, 20);
+ EXPECT_EQ(quad.br.x, 20);
+ EXPECT_EQ(quad.br.y, 20);
}
// width x textSize
@@ -108,16 +92,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -30);
- ASSERT_EQ(quad.tl.y, -5);
- ASSERT_EQ(quad.tr.x, 10);
- ASSERT_EQ(quad.tr.y, -5);
- ASSERT_EQ(quad.bl.x, -30);
- ASSERT_EQ(quad.bl.y, 15);
- ASSERT_EQ(quad.br.x, 10);
- ASSERT_EQ(quad.br.y, 15);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -30);
+ EXPECT_EQ(quad.tl.y, -5);
+ EXPECT_EQ(quad.tr.x, 10);
+ EXPECT_EQ(quad.tr.y, -5);
+ EXPECT_EQ(quad.bl.x, -30);
+ EXPECT_EQ(quad.bl.y, 15);
+ EXPECT_EQ(quad.br.x, 10);
+ EXPECT_EQ(quad.br.y, 15);
}
// width x textSize + padding
@@ -130,16 +114,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -40);
- ASSERT_EQ(quad.tl.y, -10);
- ASSERT_EQ(quad.tr.x, 20);
- ASSERT_EQ(quad.tr.y, -10);
- ASSERT_EQ(quad.bl.x, -40);
- ASSERT_EQ(quad.bl.y, 20);
- ASSERT_EQ(quad.br.x, 20);
- ASSERT_EQ(quad.br.y, 20);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -40);
+ EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tr.x, 20);
+ EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.bl.x, -40);
+ EXPECT_EQ(quad.bl.y, 20);
+ EXPECT_EQ(quad.br.x, 20);
+ EXPECT_EQ(quad.br.y, 20);
}
// height
@@ -148,16 +132,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -30);
- ASSERT_EQ(quad.tl.y, -10);
- ASSERT_EQ(quad.tr.x, -10);
- ASSERT_EQ(quad.tr.y, -10);
- ASSERT_EQ(quad.bl.x, -30);
- ASSERT_EQ(quad.bl.y, 30);
- ASSERT_EQ(quad.br.x, -10);
- ASSERT_EQ(quad.br.y, 30);
+ getIconQuad(shapedIcon, layout, 24.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -30);
+ EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tr.x, -10);
+ EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.bl.x, -30);
+ EXPECT_EQ(quad.bl.y, 30);
+ EXPECT_EQ(quad.br.x, -10);
+ EXPECT_EQ(quad.br.y, 30);
}
// height x textSize
@@ -166,16 +150,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -20);
- ASSERT_EQ(quad.tl.y, -5);
- ASSERT_EQ(quad.tr.x, 0);
- ASSERT_EQ(quad.tr.y, -5);
- ASSERT_EQ(quad.bl.x, -20);
- ASSERT_EQ(quad.bl.y, 15);
- ASSERT_EQ(quad.br.x, 0);
- ASSERT_EQ(quad.br.y, 15);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -20);
+ EXPECT_EQ(quad.tl.y, -5);
+ EXPECT_EQ(quad.tr.x, 0);
+ EXPECT_EQ(quad.tr.y, -5);
+ EXPECT_EQ(quad.bl.x, -20);
+ EXPECT_EQ(quad.bl.y, 15);
+ EXPECT_EQ(quad.br.x, 0);
+ EXPECT_EQ(quad.br.y, 15);
}
// height x textSize + padding
@@ -188,16 +172,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -30);
- ASSERT_EQ(quad.tl.y, -10);
- ASSERT_EQ(quad.tr.x, 10);
- ASSERT_EQ(quad.tr.y, -10);
- ASSERT_EQ(quad.bl.x, -30);
- ASSERT_EQ(quad.bl.y, 20);
- ASSERT_EQ(quad.br.x, 10);
- ASSERT_EQ(quad.br.y, 20);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -30);
+ EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tr.x, 10);
+ EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.bl.x, -30);
+ EXPECT_EQ(quad.bl.y, 20);
+ EXPECT_EQ(quad.br.x, 10);
+ EXPECT_EQ(quad.br.y, 20);
}
// both
@@ -206,16 +190,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -60);
- ASSERT_EQ(quad.tl.y, -10);
- ASSERT_EQ(quad.tr.x, 20);
- ASSERT_EQ(quad.tr.y, -10);
- ASSERT_EQ(quad.bl.x, -60);
- ASSERT_EQ(quad.bl.y, 30);
- ASSERT_EQ(quad.br.x, 20);
- ASSERT_EQ(quad.br.y, 30);
+ getIconQuad(shapedIcon, layout, 24.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -60);
+ EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tr.x, 20);
+ EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.bl.x, -60);
+ EXPECT_EQ(quad.bl.y, 30);
+ EXPECT_EQ(quad.br.x, 20);
+ EXPECT_EQ(quad.br.y, 30);
}
// both x textSize
@@ -224,16 +208,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -30);
- ASSERT_EQ(quad.tl.y, -5);
- ASSERT_EQ(quad.tr.x, 10);
- ASSERT_EQ(quad.tr.y, -5);
- ASSERT_EQ(quad.bl.x, -30);
- ASSERT_EQ(quad.bl.y, 15);
- ASSERT_EQ(quad.br.x, 10);
- ASSERT_EQ(quad.br.y, 15);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -30);
+ EXPECT_EQ(quad.tl.y, -5);
+ EXPECT_EQ(quad.tr.x, 10);
+ EXPECT_EQ(quad.tr.y, -5);
+ EXPECT_EQ(quad.bl.x, -30);
+ EXPECT_EQ(quad.bl.y, 15);
+ EXPECT_EQ(quad.br.x, 10);
+ EXPECT_EQ(quad.br.y, 15);
}
// both x textSize + padding
@@ -246,16 +230,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -40);
- ASSERT_EQ(quad.tl.y, -10);
- ASSERT_EQ(quad.tr.x, 20);
- ASSERT_EQ(quad.tr.y, -10);
- ASSERT_EQ(quad.bl.x, -40);
- ASSERT_EQ(quad.bl.y, 20);
- ASSERT_EQ(quad.br.x, 20);
- ASSERT_EQ(quad.br.y, 20);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -40);
+ EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tr.x, 20);
+ EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.bl.x, -40);
+ EXPECT_EQ(quad.bl.y, 20);
+ EXPECT_EQ(quad.br.x, 20);
+ EXPECT_EQ(quad.br.y, 20);
}
// both x textSize + padding t/r/b/l
@@ -268,16 +252,16 @@ 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);
-
- ASSERT_EQ(quad.tl.x, -45);
- ASSERT_EQ(quad.tl.y, -5);
- ASSERT_EQ(quad.tr.x, 15);
- ASSERT_EQ(quad.tr.y, -5);
- ASSERT_EQ(quad.bl.x, -45);
- ASSERT_EQ(quad.bl.y, 25);
- ASSERT_EQ(quad.br.x, 15);
- ASSERT_EQ(quad.br.y, 25);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+
+ EXPECT_EQ(quad.tl.x, -45);
+ EXPECT_EQ(quad.tl.y, -5);
+ EXPECT_EQ(quad.tr.x, 15);
+ EXPECT_EQ(quad.tr.y, -5);
+ EXPECT_EQ(quad.bl.x, -45);
+ EXPECT_EQ(quad.bl.y, 25);
+ EXPECT_EQ(quad.br.x, 15);
+ EXPECT_EQ(quad.br.y, 25);
}
}
diff --git a/test/tile/annotation_tile.test.cpp b/test/tile/annotation_tile.test.cpp
index ac3e064c50..8f3f903925 100644
--- a/test/tile/annotation_tile.test.cpp
+++ b/test/tile/annotation_tile.test.cpp
@@ -4,14 +4,17 @@
#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/renderer/tile_parameters.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/gl/headless_backend.hpp>
+#include <mbgl/style/style.hpp>
#include <memory>
@@ -23,8 +26,12 @@ public:
TransformState transformState;
util::RunLoop loop;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager { 1.0 };
- style::Style style { threadPool, fileSource, 1.0 };
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
+ HeadlessBackend backend;
+ BackendScope scope { backend };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
TileParameters tileParameters {
1.0,
@@ -34,7 +41,9 @@ public:
fileSource,
MapMode::Continuous,
annotationManager,
- style
+ imageManager,
+ glyphManager,
+ 0
};
};
@@ -44,14 +53,13 @@ TEST(AnnotationTile, Issue8289) {
AnnotationTile tile(OverscaledTileID(0, 0, 0), test.tileParameters);
auto data = std::make_unique<AnnotationTileData>();
- data->layers.emplace("test", AnnotationTileLayer("test"));
- data->layers.at("test").features.push_back(AnnotationTileFeature(0, FeatureType::Point, GeometryCollection()));
+ 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),
+ std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
+ std::make_unique<FeatureIndex>(),
+ std::move(data),
}, 0);
auto collisionTile = std::make_unique<CollisionTile>(PlacementConfig());
@@ -62,15 +70,17 @@ TEST(AnnotationTile, Issue8289) {
collisionTile->placeFeature(feature, false, false);
tile.onPlacement(GeometryTile::PlacementResult {
+ std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
+ std::move(collisionTile),
+ {},
{},
- std::move(collisionTile),
}, 0);
// Simulate a second layout with empty data.
tile.onLayout(GeometryTile::LayoutResult {
- {},
- std::make_unique<FeatureIndex>(),
- std::make_unique<AnnotationTileData>(),
+ std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
+ std::make_unique<FeatureIndex>(),
+ std::make_unique<AnnotationTileData>(),
}, 0);
std::unordered_map<std::string, std::vector<Feature>> result;
@@ -78,7 +88,7 @@ TEST(AnnotationTile, Issue8289) {
TransformState transformState;
RenderedQueryOptions options;
- tile.queryRenderedFeatures(result, queryGeometry, transformState, options);
+ tile.queryRenderedFeatures(result, queryGeometry, transformState, {}, options);
EXPECT_TRUE(result.empty());
}
diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp
index 18d11c2c14..953c3b8a5f 100644
--- a/test/tile/geojson_tile.test.cpp
+++ b/test/tile/geojson_tile.test.cpp
@@ -7,10 +7,12 @@
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/style/style.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>
@@ -23,8 +25,10 @@ public:
TransformState transformState;
util::RunLoop loop;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager { 1.0 };
- style::Style style { threadPool, fileSource, 1.0 };
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" };
TileParameters tileParameters {
@@ -35,14 +39,16 @@ public:
fileSource,
MapMode::Continuous,
annotationManager,
- style
+ imageManager,
+ glyphManager,
+ 0
};
};
TEST(GeoJSONTile, Issue7648) {
GeoJSONTileTest test;
- test.style.addLayer(std::make_unique<CircleLayer>("circle", "source"));
+ CircleLayer layer("circle", "source");
mapbox::geometry::feature_collection<int16_t> features;
features.push_back(mapbox::geometry::feature<int16_t> {
@@ -55,9 +61,10 @@ TEST(GeoJSONTile, Issue7648) {
observer.tileChanged = [&] (const Tile&) {
// Once present, the bucket should never "disappear", which would cause
// flickering.
- ASSERT_NE(nullptr, tile.getBucket(*test.style.getRenderLayer("circle")));
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
};
+ tile.setLayers({{ layer.baseImpl }});
tile.setObserver(&observer);
tile.setPlacementConfig({});
@@ -76,7 +83,7 @@ TEST(GeoJSONTile, Issue7648) {
TEST(GeoJSONTile, Issue9927) {
GeoJSONTileTest test;
- test.style.addLayer(std::make_unique<CircleLayer>("circle", "source"));
+ CircleLayer layer("circle", "source");
mapbox::geometry::feature_collection<int16_t> features;
features.push_back(mapbox::geometry::feature<int16_t> {
@@ -85,6 +92,7 @@ TEST(GeoJSONTile, Issue9927) {
GeoJSONTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, features);
+ tile.setLayers({{ layer.baseImpl }});
tile.setPlacementConfig({});
while (!tile.isComplete()) {
@@ -92,18 +100,18 @@ TEST(GeoJSONTile, Issue9927) {
}
ASSERT_TRUE(tile.isRenderable());
- ASSERT_NE(nullptr, tile.getBucket(*test.style.getRenderLayer("circle")));
+ 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(*test.style.getRenderLayer("circle")));
+ 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(*test.style.getRenderLayer("circle")));
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
}
diff --git a/test/tile/raster_tile.test.cpp b/test/tile/raster_tile.test.cpp
index a606066e9b..8b2b3dee61 100644
--- a/test/tile/raster_tile.test.cpp
+++ b/test/tile/raster_tile.test.cpp
@@ -3,13 +3,15 @@
#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>
-#include <mbgl/style/style.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/renderer/raster_bucket.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/text/glyph_manager.hpp>
using namespace mbgl;
@@ -19,8 +21,10 @@ public:
TransformState transformState;
util::RunLoop loop;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager { 1.0 };
- style::Style style { threadPool, fileSource, 1.0 };
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" };
TileParameters tileParameters {
@@ -31,7 +35,9 @@ public:
fileSource,
MapMode::Continuous,
annotationManager,
- style
+ imageManager,
+ glyphManager,
+ 0
};
};
@@ -56,7 +62,7 @@ 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{}), 0);
+ tile.onParsed(std::make_unique<RasterBucket>(PremultipliedImage{}), 0);
EXPECT_TRUE(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 336076ecc2..7e8b659b7a 100644
--- a/test/tile/vector_tile.test.cpp
+++ b/test/tile/vector_tile.test.cpp
@@ -6,14 +6,16 @@
#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/symbol_bucket.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/text/glyph_manager.hpp>
#include <memory>
@@ -25,8 +27,10 @@ public:
TransformState transformState;
util::RunLoop loop;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager { 1.0 };
- style::Style style { threadPool, fileSource, 1.0 };
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" };
TileParameters tileParameters {
@@ -37,7 +41,9 @@ public:
fileSource,
MapMode::Continuous,
annotationManager,
- style
+ imageManager,
+ glyphManager,
+ 0
};
};
@@ -69,7 +75,7 @@ TEST(VectorTile, Issue7615) {
style::SymbolLayoutProperties::PossiblyEvaluated(),
std::map<
std::string,
- std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>(),
+ std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>(),
16.0f, 1.0f, 0.0f, false, false);
// Simulate placement of a symbol layer.
@@ -79,16 +85,18 @@ TEST(VectorTile, Issue7615) {
symbolBucket
}},
nullptr,
+ {},
+ {},
}, 0);
// Subsequent onLayout should not cause the existing symbol bucket to be discarded.
tile.onLayout(GeometryTile::LayoutResult {
- {},
+ std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
nullptr,
nullptr,
}, 0);
- EXPECT_EQ(symbolBucket.get(), tile.getBucket(*symbolLayer.baseImpl->createRenderLayer()));
+ EXPECT_EQ(symbolBucket.get(), tile.getBucket(*symbolLayer.baseImpl));
}
TEST(VectorTile, Issue8542) {
diff --git a/test/util/async_task.test.cpp b/test/util/async_task.test.cpp
index 78dc79dd19..f3025e8952 100644
--- a/test/util/async_task.test.cpp
+++ b/test/util/async_task.test.cpp
@@ -1,9 +1,12 @@
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/thread.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/test/util.hpp>
+#include <atomic>
+#include <future>
#include <vector>
using namespace mbgl::util;
@@ -29,6 +32,10 @@ public:
cb();
}
+ void sync(std::promise<void> barrier) {
+ barrier.set_value();
+ }
+
private:
AsyncTask *async;
};
@@ -94,23 +101,24 @@ TEST(AsyncTask, DestroyAfterSignaling) {
TEST(AsyncTask, RequestCoalescingMultithreaded) {
RunLoop loop;
- unsigned count = 0;
+ unsigned count = 0, numThreads = 25;
AsyncTask async([&count] { ++count; });
- std::vector<std::unique_ptr<Thread<TestWorker>>> threads;
- ThreadContext context = {"Test"};
+ mbgl::ThreadPool threads(numThreads);
+ auto mailbox = std::make_shared<mbgl::Mailbox>(threads);
- unsigned numThreads = 25;
- for (unsigned i = 0; i < numThreads; ++i) {
- std::unique_ptr<Thread<TestWorker>> thread =
- std::make_unique<Thread<TestWorker>>(context, &async);
+ TestWorker worker(&async);
+ mbgl::ActorRef<TestWorker> workerRef(worker, mailbox);
- thread->invoke(&TestWorker::run);
- threads.push_back(std::move(thread));
+ for (unsigned i = 0; i < numThreads; ++i) {
+ workerRef.invoke(&TestWorker::run);
}
- // Join all the threads
- threads.clear();
+ std::promise<void> barrier;
+ std::future<void> barrierFuture = barrier.get_future();
+
+ workerRef.invoke(&TestWorker::sync, std::move(barrier));
+ barrierFuture.wait();
loop.runOnce();
@@ -120,29 +128,20 @@ TEST(AsyncTask, RequestCoalescingMultithreaded) {
TEST(AsyncTask, ThreadSafety) {
RunLoop loop;
- unsigned count = 0;
- AsyncTask async([&count] { ++count; });
+ unsigned count = 0, numThreads = 25;
+ std::atomic_uint completed(numThreads);
- unsigned numThreads = 25;
+ AsyncTask async([&count] { ++count; });
- auto callback = [&] {
- if (!--numThreads) {
- loop.stop();
- }
- };
+ mbgl::ThreadPool threads(numThreads);
+ auto mailbox = std::make_shared<mbgl::Mailbox>(threads);
- std::vector<std::unique_ptr<Thread<TestWorker>>> threads;
- std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests;
- ThreadContext context = {"Test"};
+ TestWorker worker(&async);
+ mbgl::ActorRef<TestWorker> workerRef(worker, mailbox);
for (unsigned i = 0; i < numThreads; ++i) {
- std::unique_ptr<Thread<TestWorker>> thread =
- std::make_unique<Thread<TestWorker>>(context, &async);
-
- requests.push_back(
- thread->invokeWithCallback(&TestWorker::runWithCallback, callback));
-
- threads.push_back(std::move(thread));
+ // The callback runs on the worker, thus the atomic type.
+ workerRef.invoke(&TestWorker::runWithCallback, [&] { if (!--completed) loop.stop(); });
}
loop.run();
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/image.test.cpp b/test/util/image.test.cpp
index 4cacf89253..f4a6473040 100644
--- a/test/util/image.test.cpp
+++ b/test/util/image.test.cpp
@@ -86,33 +86,49 @@ TEST(Image, WebPTile) {
}
#endif // !defined(__ANDROID__) && !defined(__APPLE__) && !defined(QT_IMAGE_DECODERS)
+TEST(Image, Resize) {
+ AlphaImage image({0, 0});
+
+ image.resize({1, 1});
+ EXPECT_EQ(image.size, Size({1, 1}));
+
+ image.fill(100);
+ image.resize({2, 1});
+ EXPECT_EQ(image.size, Size({2, 1}));
+ EXPECT_EQ(image.data[0], 100);
+ EXPECT_EQ(image.data[1], 0);
+
+ image.resize({0, 0});
+ EXPECT_EQ(image.size, Size({0, 0}));
+}
+
TEST(Image, Copy) {
PremultipliedImage src5({5, 5});
PremultipliedImage dst5({5, 5});
PremultipliedImage src10({10, 10});
PremultipliedImage dst10({10, 10});
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {0, 6}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {0, 5}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {1, 6}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {1, 5}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {0, 6}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {0, 5}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {1, 6}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {1, 5}), std::out_of_range);
const uint32_t max = std::numeric_limits<uint32_t>::max();
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {0, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {0, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {1, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {1, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {0, max}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 0}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {0, max}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {1, max}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {1, max}), std::out_of_range);
}
TEST(Image, Move) {
@@ -142,4 +158,8 @@ TEST(Image, Premultiply) {
EXPECT_EQ(127, image.data[1]);
EXPECT_EQ(127, image.data[2]);
EXPECT_EQ(128, image.data[3]);
+ EXPECT_EQ(1u, image.size.width);
+ EXPECT_EQ(1u, image.size.height);
+ EXPECT_EQ(0u, rgba.size.width);
+ EXPECT_EQ(0u, rgba.size.height);
}
diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp
index 065d024bef..54763cd9db 100644
--- a/test/util/memory.test.cpp
+++ b/test/util/memory.test.cpp
@@ -3,12 +3,11 @@
#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>
+#include <mbgl/style/style.hpp>
#include <algorithm>
#include <iostream>
@@ -17,7 +16,7 @@
#include <unordered_map>
#include <utility>
-#include <stdlib.h>
+#include <cstdlib>
#include <unistd.h>
using namespace mbgl;
@@ -35,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 };
@@ -72,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::Still);
map.setZoom(16); // more map features
- map.setStyleURL("mapbox://streets");
+ 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);
- map.setStyleURL("mapbox://satellite");
+ HeadlessFrontend frontend { { 256, 256 }, ratio, test.fileSource, test.threadPool };
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), ratio, test.fileSource,
+ test.threadPool, MapMode::Still);
+ map.getStyle().loadURL("mapbox://satellite");
- test::render(map, test.view);
+ frontend.render(map);
}
/**
@@ -113,53 +115,54 @@ TEST(Memory, Footprint) {
if (!shouldRunFootprint()) {
return;
}
-
+
MemoryTest test;
- auto renderMap = [&](Map& map, const char* style){
- map.setZoom(16);
- map.setStyleURL(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::Still) {
+ 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);
RecordProperty("vectorFootprint", vectorFootprint);
RecordProperty("rasterFootprint", rasterFootprint);
- ASSERT_LT(vectorFootprint, 65.2 * 1024 * 1024) << "\
+ ASSERT_LT(vectorFootprint, 40 * 1024 * 1024) << "\
mbgl::Map footprint over 65.2MB for vector styles.";
ASSERT_LT(rasterFootprint, 25 * 1024 * 1024) << "\
diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp
index 8a3a400887..d3a2ebae03 100644
--- a/test/util/merge_lines.test.cpp
+++ b/test/util/merge_lines.test.cpp
@@ -1,50 +1,31 @@
#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>
+#include <utility>
const std::u16string aaa = u"a";
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(id_),
- type(type_),
- geometry(geometry_),
- properties(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_;
}
};
@@ -52,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);
@@ -78,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);
@@ -98,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);
@@ -117,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);
@@ -128,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 feaabf2630..09c940c4c3 100644
--- a/test/util/offscreen_texture.test.cpp
+++ b/test/util/offscreen_texture.test.cpp
@@ -3,23 +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 });
- view.bind();
+
+ // Scissor test shouldn't leak after HeadlessBackend::bind().
+ MBGL_CHECK_ERROR(glScissor(64, 64, 128, 128));
+ backend.getContext().scissorTest.setCurrentValue(true);
+
+ 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);
}
@@ -35,7 +38,7 @@ struct Shader {
MBGL_CHECK_ERROR(glCompileShader(fragmentShader));
MBGL_CHECK_ERROR(glAttachShader(program, fragmentShader));
MBGL_CHECK_ERROR(glLinkProgram(program));
- a_pos = glGetAttribLocation(program, "a_pos");
+ a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
~Shader() {
@@ -69,7 +72,7 @@ struct Buffer {
TEST(OffscreenTexture, RenderToTexture) {
- HeadlessBackend backend { test::sharedDisplay() };
+ HeadlessBackend backend({ 512, 256 });
BackendScope scope { backend };
auto& context = backend.getContext();
@@ -114,13 +117,12 @@ void main() {
}
)MBGL_SHADER");
- GLuint u_texture = glGetUniformLocation(compositeShader.program, "u_texture");
+ GLuint u_texture = MBGL_CHECK_ERROR(glGetUniformLocation(compositeShader.program, "u_texture"));
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(), {}, {});
@@ -128,6 +130,11 @@ void main() {
// Then, create a texture, bind it, and render yellow to that texture. This should not
// affect the originally bound FBO.
OffscreenTexture texture(context, { 128, 128 });
+
+ // Scissor test shouldn't leak after OffscreenTexture::bind().
+ MBGL_CHECK_ERROR(glScissor(32, 32, 64, 64));
+ context.scissorTest.setCurrentValue(true);
+
texture.bind();
context.clear(Color(), {}, {});
@@ -143,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.
@@ -158,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.test.cpp b/test/util/thread.test.cpp
index 972bddf383..76fb5ce3f0 100644
--- a/test/util/thread.test.cpp
+++ b/test/util/thread.test.cpp
@@ -1,63 +1,58 @@
-#include <mbgl/util/thread.hpp>
-#include <mbgl/util/run_loop.hpp>
-
+#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/test/util.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/timer.hpp>
#include <atomic>
+#include <memory>
+using namespace mbgl;
using namespace mbgl::util;
class TestObject {
public:
- TestObject(std::thread::id otherTid)
+ TestObject(ActorRef<TestObject>, std::thread::id otherTid)
: tid(std::this_thread::get_id()) {
EXPECT_NE(tid, otherTid);
}
- void fn1(int val) {
+ ~TestObject() {
EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(val, 1);
}
- void fn2(std::function<void (int)> cb) {
+ void fn1(int val) const {
EXPECT_EQ(tid, std::this_thread::get_id());
- cb(1);
- }
-
- void transferIn(std::unique_ptr<int> val) {
- EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(*val, 1);
+ EXPECT_EQ(val, 1);
}
- void transferOut(std::function<void (std::unique_ptr<int>)> cb) {
+ void fn2(std::function<void (int)> cb) const {
EXPECT_EQ(tid, std::this_thread::get_id());
- cb(std::make_unique<int>(1));
+ cb(1);
}
- void transferInOut(std::unique_ptr<int> val, std::function<void (std::unique_ptr<int>)> cb) {
+ void transferIn(std::unique_ptr<int> val) const {
EXPECT_EQ(tid, std::this_thread::get_id());
EXPECT_EQ(*val, 1);
- cb(std::move(val));
}
- void transferInShared(std::shared_ptr<int> val) {
+ void transferInShared(std::shared_ptr<int> val) const {
EXPECT_EQ(tid, std::this_thread::get_id());
EXPECT_EQ(*val, 1);
}
- void transferOutShared(std::function<void (std::shared_ptr<int>)> cb) {
+ void transferString(const std::string& string) const {
EXPECT_EQ(tid, std::this_thread::get_id());
- cb(std::make_shared<int>(1));
+ EXPECT_EQ(string, "test");
}
- void transferString(const std::string& string, std::function<void (std::string)> cb) {
- EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(string, "test");
- cb(string);
+ void checkContext(std::promise<bool> result) const {
+ result.set_value(tid == std::this_thread::get_id());
}
- void checkContext(std::function<void (bool)> cb) const {
- cb(tid == std::this_thread::get_id());
+ void sync(std::promise<void> result) const {
+ result.set_value();
}
const std::thread::id tid;
@@ -65,95 +60,61 @@ public:
TEST(Thread, invoke) {
const std::thread::id tid = std::this_thread::get_id();
+ Thread<TestObject> thread("Test", tid);
- RunLoop loop;
- std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests;
+ thread.actor().invoke(&TestObject::fn1, 1);
+ thread.actor().invoke(&TestObject::fn2, [] (int result) { EXPECT_EQ(result, 1); } );
+ thread.actor().invoke(&TestObject::transferIn, std::make_unique<int>(1));
+ thread.actor().invoke(&TestObject::transferInShared, std::make_shared<int>(1));
- loop.invoke([&] {
- EXPECT_EQ(tid, std::this_thread::get_id());
- Thread<TestObject> thread({"Test"}, tid);
-
- thread.invoke(&TestObject::fn1, 1);
- requests.push_back(thread.invokeWithCallback(&TestObject::fn2, [&] (int result) {
- EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(result, 1);
- }));
-
- thread.invoke(&TestObject::transferIn, std::make_unique<int>(1));
- requests.push_back(thread.invokeWithCallback(&TestObject::transferOut, [&] (std::unique_ptr<int> result) {
- EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(*result, 1);
- }));
-
- requests.push_back(thread.invokeWithCallback(&TestObject::transferInOut, std::make_unique<int>(1), [&] (std::unique_ptr<int> result) {
- EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(*result, 1);
- }));
-
- thread.invoke(&TestObject::transferInShared, std::make_shared<int>(1));
- requests.push_back(thread.invokeWithCallback(&TestObject::transferOutShared, [&] (std::shared_ptr<int> result) {
- EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(*result, 1);
- }));
-
- // Cancelled request
- thread.invokeWithCallback(&TestObject::fn2, [&] (int) {
- ADD_FAILURE();
- });
+ std::string test("test");
+ thread.actor().invoke(&TestObject::transferString, test);
- std::string test("test");
- requests.push_back(thread.invokeWithCallback(&TestObject::transferString, test, [&] (std::string result){
- EXPECT_EQ(tid, std::this_thread::get_id());
- EXPECT_EQ(result, "test");
- loop.stop();
- }));
- test.clear();
- });
-
- loop.run();
+ // Make sure the message queue was consumed before ending the test.
+ std::promise<void> result;
+ auto resultFuture = result.get_future();
+ thread.actor().invoke(&TestObject::sync, std::move(result));
+ resultFuture.get();
}
-TEST(Thread, context) {
+TEST(Thread, Context) {
const std::thread::id tid = std::this_thread::get_id();
+ Thread<TestObject> thread("Test", tid);
- RunLoop loop;
- std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests;
-
- loop.invoke([&] {
- Thread<TestObject> thread({"Test"}, tid);
-
- requests.push_back(thread.invokeWithCallback(&TestObject::checkContext, [&] (bool inTestThreadContext) {
- EXPECT_EQ(inTestThreadContext, true);
- loop.stop();
- }));
- });
+ std::promise<bool> result;
+ auto resultFuture = result.get_future();
- loop.run();
+ thread.actor().invoke(&TestObject::checkContext, std::move(result));
+ EXPECT_EQ(resultFuture.get(), true);
}
class TestWorker {
public:
- TestWorker() = default;
+ TestWorker(ActorRef<TestWorker>) {}
- void send(std::function<void ()> fn, std::function<void ()> cb) {
- fn();
+ void send(std::function<void ()> cb) {
cb();
}
+
+ void sendDelayed(std::function<void ()> cb) {
+ timer.start(Milliseconds(300), mbgl::Duration::zero(), [cb] {
+ cb();
+ });
+ }
+
+private:
+ Timer timer;
};
TEST(Thread, ExecutesAfter) {
RunLoop loop;
- Thread<TestWorker> thread({"Test"});
+ Thread<TestWorker> thread("Test");
bool didWork = false;
bool didAfter = false;
- auto request = thread.invokeWithCallback(&TestWorker::send, [&] {
- didWork = true;
- }, [&] {
- didAfter = true;
- loop.stop();
- });
+ thread.actor().invoke(&TestWorker::send, [&] { didWork = true; });
+ thread.actor().invoke(&TestWorker::send, [&] { didAfter = true; loop.stop(); });
loop.run();
@@ -161,72 +122,92 @@ TEST(Thread, ExecutesAfter) {
EXPECT_TRUE(didAfter);
}
-TEST(Thread, WorkRequestDeletionWaitsForWorkToComplete) {
+TEST(Thread, CanSelfWakeUp) {
RunLoop loop;
+ Thread<TestWorker> thread("Test");
- Thread<TestWorker> thread({"Test"});
+ thread.actor().invoke(&TestWorker::sendDelayed, [&] {
+ loop.stop();
+ });
- std::promise<void> started;
- bool didWork = false;
+ loop.run();
+}
- auto request = thread.invokeWithCallback(&TestWorker::send, [&] {
- started.set_value();
- usleep(10000);
- didWork = true;
- }, [&] {});
+TEST(Thread, Concurrency) {
+ auto loop = std::make_shared<RunLoop>();
+
+ unsigned numMessages = 100000;
+ std::atomic_uint completed(numMessages);
+
+ ThreadPool threadPool(10);
+ Actor<TestWorker> poolWorker(threadPool);
+ auto poolWorkerRef = poolWorker.self();
+
+ Thread<TestWorker> threadedObject("Test");
+ auto threadedObjectRef = threadedObject.actor();
+
+ // 10 threads sending 100k messages to the Thread. The
+ // idea here is to test if the scheduler is handling concurrency
+ // correctly, otherwise this test should crash.
+ for (unsigned i = 0; i < numMessages; ++i) {
+ poolWorkerRef.invoke(&TestWorker::send, [threadedObjectRef, loop, &completed] () mutable {
+ threadedObjectRef.invoke(&TestWorker::send, [loop, &completed] () {
+ if (!--completed) {
+ loop->stop();
+ }
+ });
+ });
+ };
- started.get_future().get();
- request.reset();
- EXPECT_TRUE(didWork);
+ loop->run();
}
-TEST(Thread, WorkRequestDeletionCancelsAfter) {
- RunLoop loop;
- Thread<TestWorker> thread({"Test"});
+TEST(Thread, ThreadPoolMessaging) {
+ auto loop = std::make_shared<RunLoop>();
- std::promise<void> started;
- bool didAfter = false;
+ ThreadPool threadPool(1);
+ Actor<TestWorker> poolWorker(threadPool);
+ auto poolWorkerRef = poolWorker.self();
+
+ Thread<TestWorker> threadedObject("Test");
+ auto threadedObjectRef = threadedObject.actor();
- auto request = thread.invokeWithCallback(&TestWorker::send, [&] {
- started.set_value();
- }, [&] {
- didAfter = true;
+ // This is sending a message to the Thread from the main
+ // thread. Then the Thread will send another message to
+ // a worker on the ThreadPool.
+ threadedObjectRef.invoke(&TestWorker::send, [poolWorkerRef, loop] () mutable {
+ poolWorkerRef.invoke(&TestWorker::send, [loop] () { loop->stop(); });
});
- started.get_future().get();
- request.reset();
- loop.runOnce();
- EXPECT_FALSE(didAfter);
-}
+ loop->run();
-TEST(Thread, WorkRequestDeletionCancelsImmediately) {
- RunLoop loop;
- Thread<TestWorker> thread({"Test"});
+ // Same as before, but in the opposite direction.
+ poolWorkerRef.invoke(&TestWorker::send, [threadedObjectRef, loop] () mutable {
+ threadedObjectRef.invoke(&TestWorker::send, [loop] () { loop->stop(); });
+ });
- std::promise<void> started;
+ loop->run();
+}
+
+TEST(Thread, ReferenceCanOutliveThread) {
+ auto thread = std::make_unique<Thread<TestWorker>>("Test");
+ auto worker = thread->actor();
- auto request1 = thread.invokeWithCallback(&TestWorker::send, [&] {
- usleep(10000);
- started.set_value();
- }, [&] {});
+ thread.reset();
- auto request2 = thread.invokeWithCallback(&TestWorker::send, [&] {
- ADD_FAILURE() << "Second work item should not be invoked";
- }, [&] {});
- request2.reset();
+ for (unsigned i = 0; i < 1000; ++i) {
+ worker.invoke(&TestWorker::send, [&] { ADD_FAILURE() << "Should never happen"; });
+ }
- started.get_future().get();
- request1.reset();
+ usleep(10000);
}
TEST(Thread, DeletePausedThread) {
- RunLoop loop;
-
std::atomic_bool flag(false);
- auto thread = std::make_unique<Thread<TestWorker>>(ThreadContext{"Test"});
+ auto thread = std::make_unique<Thread<TestWorker>>("Test");
thread->pause();
- thread->invoke(&TestWorker::send, [&] { flag = true; }, [] {});
+ thread->actor().invoke(&TestWorker::send, [&] { flag = true; });
// Should not hang.
thread.reset();
@@ -240,18 +221,18 @@ TEST(Thread, Pause) {
std::atomic_bool flag(false);
- Thread<TestWorker> thread1({"Test1"});
+ Thread<TestWorker> thread1("Test1");
thread1.pause();
- Thread<TestWorker> thread2({"Test2"});
+ Thread<TestWorker> thread2("Test2");
for (unsigned i = 0; i < 100; ++i) {
- thread1.invoke(&TestWorker::send, [&] { flag = true; }, [] {});
- thread2.invoke(&TestWorker::send, [&] { ASSERT_FALSE(flag); }, [] {});
+ thread1.actor().invoke(&TestWorker::send, [&] { flag = true; });
+ thread2.actor().invoke(&TestWorker::send, [&] { ASSERT_FALSE(flag); });
}
// Queue a message at the end of thread2 queue.
- thread2.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {});
+ thread2.actor().invoke(&TestWorker::send, [&] { loop.stop(); });
loop.run();
}
@@ -260,16 +241,16 @@ TEST(Thread, Resume) {
std::atomic_bool flag(false);
- Thread<TestWorker> thread({"Test"});
+ Thread<TestWorker> thread("Test");
thread.pause();
for (unsigned i = 0; i < 100; ++i) {
- thread.invoke(&TestWorker::send, [&] { flag = true; }, [] {});
+ thread.actor().invoke(&TestWorker::send, [&] { flag = true; });
}
// Thread messages are ondered, when we resume, this is going
// to me the last thing to run on the message queue.
- thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {});
+ thread.actor().invoke(&TestWorker::send, [&] { loop.stop(); });
// This test will be flaky if the thread doesn't get paused.
ASSERT_FALSE(flag);
@@ -283,7 +264,7 @@ TEST(Thread, Resume) {
TEST(Thread, PauseResume) {
RunLoop loop;
- Thread<TestWorker> thread({"Test"});
+ Thread<TestWorker> thread("Test");
// Test if multiple pause/resume work.
for (unsigned i = 0; i < 100; ++i) {
@@ -291,6 +272,6 @@ TEST(Thread, PauseResume) {
thread.resume();
}
- thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {});
+ thread.actor().invoke(&TestWorker::send, [&] { loop.stop(); });
loop.run();
}
diff --git a/test/util/thread_local.test.cpp b/test/util/thread_local.test.cpp
index 4ee7042580..7142697f48 100644
--- a/test/util/thread_local.test.cpp
+++ b/test/util/thread_local.test.cpp
@@ -4,13 +4,15 @@
#include <mbgl/test/util.hpp>
+#include <future>
+
using namespace mbgl::util;
namespace {
class TestThread {
public:
- TestThread(int *number_) {
+ TestThread(mbgl::ActorRef<TestThread>, int *number_) {
number.set(number_);
}
@@ -18,8 +20,8 @@ public:
number.set(nullptr);
}
- int getNumber() {
- return *number.get();
+ void getNumber(std::promise<int> result){
+ result.set_value(*number.get());
}
private:
@@ -37,15 +39,28 @@ TEST(ThreadLocalStorage, Basic) {
int number2 = 2;
int number3 = 3;
- ThreadContext context = {"Test"};
+ Thread<TestThread> thread1("Test", &number1);
+ Thread<TestThread> thread2("Test", &number2);
+ Thread<TestThread> thread3("Test", &number3);
+
+ auto thread1Ref = thread1.actor();
+ auto thread2Ref = thread2.actor();
+ auto thread3Ref = thread3.actor();
+
+ std::promise<int> result1;
+ auto result1Future = result1.get_future();
+ thread1Ref.invoke(&TestThread::getNumber, std::move(result1));
+ EXPECT_EQ(number1, result1Future.get());
- Thread<TestThread> thread1(context, &number1);
- Thread<TestThread> thread2(context, &number2);
- Thread<TestThread> thread3(context, &number3);
+ std::promise<int> result2;
+ auto result2Future = result2.get_future();
+ thread2Ref.invoke(&TestThread::getNumber, std::move(result2));
+ EXPECT_EQ(number2, result2Future.get());
- EXPECT_EQ(number1, thread1.invokeSync(&TestThread::getNumber));
- EXPECT_EQ(number2, thread2.invokeSync(&TestThread::getNumber));
- EXPECT_EQ(number3, thread3.invokeSync(&TestThread::getNumber));
+ std::promise<int> result3;
+ auto result3Future = result3.get_future();
+ thread3Ref.invoke(&TestThread::getNumber, std::move(result3));
+ EXPECT_EQ(number3, result3Future.get());
}
TEST(ThreadLocalStorage, NotSetReturnsNull) {
@@ -56,40 +71,38 @@ TEST(ThreadLocalStorage, NotSetReturnsNull) {
namespace {
-struct DtorCounter {
- ~DtorCounter() { ++(*value); }
- unsigned *value;
-};
-
-class TestThreadReclaim {
+class TestThreadDataOwnership {
public:
- 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 };
-
- ThreadContext context = {"Test"};
+ auto data1 = std::make_unique<int>(10);
+ auto data2 = std::make_unique<int>(20);
- auto thread1 = std::make_unique<Thread<TestThreadReclaim>>(context, dtorCounter1);
- auto thread2 = std::make_unique<Thread<TestThreadReclaim>>(context, 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/work_queue.test.cpp b/test/util/work_queue.test.cpp
deleted file mode 100644
index 60c72f7358..0000000000
--- a/test/util/work_queue.test.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#include <mbgl/test/util.hpp>
-
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/thread.hpp>
-#include <mbgl/util/work_queue.hpp>
-
-#include <thread>
-
-using namespace mbgl::util;
-
-class TestThread {
-public:
- TestThread(WorkQueue* queue_) : queue(queue_) {}
-
- void send(std::function<void()>&& fn) {
- queue->push(std::move(fn));
- }
-
-private:
- WorkQueue* queue;
-};
-
-TEST(WorkQueue, push) {
- RunLoop loop;
-
- WorkQueue queue;
- Thread<TestThread> thread({"Test"}, &queue);
-
- uint8_t count = 0;
-
- auto endTest = [&]() {
- if (++count == 4) {
- loop.stop();
- }
- };
-
- thread.invoke(&TestThread::send, endTest);
- thread.invoke(&TestThread::send, endTest);
- thread.invoke(&TestThread::send, endTest);
- thread.invoke(&TestThread::send, endTest);
-
- loop.run();
-}
-
-TEST(WorkQueue, cancel) {
- RunLoop loop;
-
- WorkQueue queue;
-
- auto work = [&]() {
- FAIL() << "Should never be called";
- };
-
- queue.push(work);
- queue.push(work);
- queue.push(work);
- queue.push(work);
- queue.push(work);
-}