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.txt65
-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--Makefile119
-rw-r--r--README.md11
-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/parse/tile_mask.benchmark.cpp38
-rw-r--r--benchmark/parse/vector_tile.benchmark.cpp28
-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.yml513
-rw-r--r--cloudformation/travis.template34
-rw-r--r--cmake/benchmark-files.cmake8
-rw-r--r--cmake/benchmark.cmake12
-rw-r--r--cmake/core-files.cmake172
-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.hpp (renamed from src/mbgl/actor/message.hpp)34
-rw-r--r--include/mbgl/actor/scheduler.hpp15
-rw-r--r--include/mbgl/annotation/annotation.hpp42
-rw-r--r--include/mbgl/map/map.hpp88
-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.hpp53
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp (renamed from include/mbgl/map/backend.hpp)44
-rw-r--r--include/mbgl/renderer/renderer_frontend.hpp31
-rw-r--r--include/mbgl/storage/default_file_source.hpp21
-rw-r--r--include/mbgl/storage/online_file_source.hpp8
-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.hpp201
-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.hpp13
-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.hpp144
-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.hpp3
-rw-r--r--include/mbgl/util/constants.hpp5
-rw-r--r--include/mbgl/util/convert.hpp6
-rw-r--r--include/mbgl/util/image.hpp23
-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/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/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.json8
-rw-r--r--platform/android/.gitignore1
-rw-r--r--platform/android/CHANGELOG.md6
-rw-r--r--platform/android/MapboxGLAndroidSDK/.gitignore2
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle16
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle59
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties2
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml37
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml44
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/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/IconFactory.java2
-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/geometry/LatLngQuad.java87
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java205
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequest.java238
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java19
-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/Annotations.java25
-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.java121
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java175
-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/MapboxMapOptions.java91
-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.java126
-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/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/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/Property.java47
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java73
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java12
-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/res-public/values/public.xml7
-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_view_image_marker.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml4
-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)0
-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.gradle12
-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.java162
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml45
-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.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java2
-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/feature/QueryRenderedFeaturesBoxCountActivity.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java2
-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/maplayout/BottomSheetActivity.java285
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java13
-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/VisibilityChangeActivity.java146
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java15
-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/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/ZoomFunctionSymbolLayerActivity.java123
-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/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/marker.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml13
-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_circle_layer.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml2
-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_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_visibility.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_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_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_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_double_map.xml1
-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_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/values/dimens.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml68
-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/config.cmake13
-rw-r--r--platform/android/dependencies.gradle5
-rw-r--r--platform/android/gradle-lint.gradle30
-rw-r--r--platform/android/settings.gradle4
-rw-r--r--platform/android/src/android_renderer_frontend.cpp74
-rw-r--r--platform/android/src/android_renderer_frontend.hpp51
-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/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.cpp8
-rwxr-xr-xplatform/android/src/jni.cpp2
-rwxr-xr-xplatform/android/src/native_map_view.cpp197
-rwxr-xr-xplatform/android/src/native_map_view.hpp48
-rw-r--r--platform/android/src/run_loop.cpp24
-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/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.cpp4
-rw-r--r--platform/android/src/style/layers/custom_layer.hpp2
-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.cpp7
-rw-r--r--platform/android/src/style/layers/symbol_layer.hpp2
-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/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/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.mm6
-rw-r--r--platform/darwin/src/MGLSource.h2
-rw-r--r--platform/darwin/src/MGLSource.mm22
-rw-r--r--platform/darwin/src/MGLSource_Private.h20
-rw-r--r--platform/darwin/src/MGLStyle.h85
-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.h86
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm45
-rw-r--r--platform/darwin/src/MGLVectorSource.mm6
-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.mm57
-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.cpp175
-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.cpp44
-rw-r--r--platform/default/mbgl/gl/headless_backend.hpp18
-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.hpp47
-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/storage/offline_database.cpp197
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp1
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp85
-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.cpp52
-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.cpp146
-rw-r--r--platform/glfw/glfw_view.hpp26
-rw-r--r--platform/glfw/main.cpp42
-rw-r--r--platform/glfw/ny_route.hpp104
-rw-r--r--platform/ios/CHANGELOG.md12
-rw-r--r--platform/ios/config.cmake5
-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/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.mm123
-rw-r--r--platform/ios/src/MGLMapView_Private.h3
-rw-r--r--platform/ios/src/Mapbox.h1
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.h4
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.mm14
-rw-r--r--platform/linux/README.md9
-rw-r--r--platform/linux/config.cmake7
-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_glx.cpp5
-rw-r--r--platform/macos/CHANGELOG.md12
-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.xib6
-rw-r--r--platform/macos/app/MapDocument.m90
-rw-r--r--platform/macos/config.cmake16
-rw-r--r--platform/macos/docs/guides/For Style Authors.md10
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj16
-rwxr-xr-xplatform/macos/scripts/create_scheme.sh40
-rw-r--r--platform/macos/src/MGLMapView.h12
-rw-r--r--platform/macos/src/MGLMapView.mm109
-rw-r--r--platform/macos/src/MGLMapView_Private.h3
-rw-r--r--platform/macos/src/Mapbox.h1
-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_map.cpp126
-rw-r--r--platform/node/src/node_map.hpp19
-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.json70
-rw-r--r--platform/node/test/js/map.test.js1
-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.js9
-rw-r--r--platform/node/test/suite_implementation.js7
-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.cpp257
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp16
-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.js9
-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/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.cpp157
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp29
-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.hpp49
-rw-r--r--src/mbgl/geometry/anchor.hpp2
-rw-r--r--src/mbgl/geometry/binpack.hpp101
-rw-r--r--src/mbgl/geometry/feature_index.cpp16
-rw-r--r--src/mbgl/geometry/feature_index.hpp9
-rw-r--r--src/mbgl/gl/attribute.cpp30
-rw-r--r--src/mbgl/gl/attribute.hpp23
-rw-r--r--src/mbgl/gl/context.cpp72
-rw-r--r--src/mbgl/gl/context.hpp22
-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/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.cpp68
-rw-r--r--src/mbgl/gl/value.hpp35
-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.cpp263
-rw-r--r--src/mbgl/layout/symbol_layout.hpp31
-rw-r--r--src/mbgl/layout/symbol_projection.cpp324
-rw-r--r--src/mbgl/layout/symbol_projection.hpp25
-rw-r--r--src/mbgl/map/backend.cpp58
-rw-r--r--src/mbgl/map/map.cpp685
-rw-r--r--src/mbgl/map/transform.cpp8
-rw-r--r--src/mbgl/map/transform_state.cpp14
-rw-r--r--src/mbgl/map/transform_state.hpp4
-rw-r--r--src/mbgl/map/update.hpp10
-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.cpp177
-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.cpp77
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp (renamed from src/mbgl/renderer/render_custom_layer.hpp)19
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp179
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp (renamed from src/mbgl/renderer/render_fill_extrusion_layer.hpp)14
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp203
-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.cpp93
-rw-r--r--src/mbgl/renderer/paint_parameters.hpp66
-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.cpp142
-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.hpp10
-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.hpp7
-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.hpp60
-rw-r--r--src/mbgl/renderer/render_static_data.cpp67
-rw-r--r--src/mbgl/renderer/render_static_data.hpp38
-rw-r--r--src/mbgl/renderer/render_style.cpp449
-rw-r--r--src/mbgl/renderer/render_style.hpp92
-rw-r--r--src/mbgl/renderer/render_style_observer.hpp14
-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.cpp78
-rw-r--r--src/mbgl/renderer/renderer_backend.cpp69
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp361
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp67
-rw-r--r--src/mbgl/renderer/renderer_observer.hpp35
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp76
-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.cpp140
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp40
-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.hpp29
-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.hpp32
-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.cpp49
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp58
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp13
-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.cpp749
-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.hpp15
-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.hpp9
-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.cpp363
-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.cpp1
-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.hpp19
-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.cpp114
-rw-r--r--src/mbgl/text/shaping.hpp23
-rw-r--r--src/mbgl/tile/geojson_tile.cpp51
-rw-r--r--src/mbgl/tile/geometry_tile.cpp133
-rw-r--r--src/mbgl/tile/geometry_tile.hpp76
-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.hpp28
-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.hpp33
-rw-r--r--src/mbgl/tile/tile_id_io.cpp5
-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/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.hpp13
-rw-r--r--src/mbgl/util/offscreen_texture.cpp1
-rw-r--r--src/mbgl/util/offscreen_texture.hpp5
-rw-r--r--src/mbgl/util/premultiply.cpp2
-rw-r--r--src/mbgl/util/thread.hpp208
-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/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.cpp201
-rw-r--r--test/actor/actor_ref.test.cpp58
-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.cpp65
-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/prefetch/empty.json5
-rw-r--r--test/fixtures/map/prefetch/expected.pngbin0 -> 2198 bytes
-rw-r--r--test/fixtures/map/prefetch/style.json24
-rw-r--r--test/fixtures/map/prefetch/tile_green.pngbin0 -> 659 bytes
-rw-r--r--test/fixtures/map/prefetch/tile_red.pngbin0 -> 659 bytes
-rw-r--r--test/fixtures/offline_database/v5.dbbin0 -> 19456 bytes
-rw-r--r--test/fixtures/offline_database/v999.dbbin0 -> 19456 bytes
-rw-r--r--test/fixtures/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/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.cpp481
-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.cpp18
-rw-r--r--test/renderer/group_by_layout.test.cpp2
-rw-r--r--test/renderer/image_manager.test.cpp147
-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.cpp135
-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_database.test.cpp93
-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.cpp272
-rw-r--r--test/tile/annotation_tile.test.cpp50
-rw-r--r--test/tile/geojson_tile.test.cpp19
-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/work_queue.test.cpp59
1000 files changed, 31156 insertions, 29311 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 8144d880ce..64dde928c5 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(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,20 +68,61 @@ 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_COMPILER_IS_GNUCXX)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unknown-warning-option")
+elseif(CMAKE_COMPILER_IS_GNUCXX)
# https://svn.boost.org/trac/boost/ticket/9240
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals")
endif()
+# Technique from https://crascit.com/2016/04/09/using-ccache-with-cmake/
+find_program(CCACHE_PROGRAM ccache)
+if(CCACHE_PROGRAM)
+ set(C_LAUNCHER "${CCACHE_PROGRAM}")
+ set(CXX_LAUNCHER "${CCACHE_PROGRAM}")
+
+ if(CMAKE_GENERATOR STREQUAL "Xcode")
+ # Set Xcode project attributes to route compilation and linking through our scripts
+ # Xcode doesn't include the path to the compiler/linker by default, so we'll have to add it.
+ configure_file(scripts/launch-c-xcode.in launch-c @ONLY)
+ configure_file(scripts/launch-cxx-xcode.in launch-cxx @ONLY)
+
+ set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c")
+ set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx")
+ set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c")
+ set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx")
+ else()
+ # Support Unix Makefiles and Ninja
+ configure_file(scripts/launch-c.in launch-c @ONLY)
+ configure_file(scripts/launch-cxx.in launch-cxx @ONLY)
+
+ set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-c")
+ set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-cxx")
+ endif()
+
+ execute_process(COMMAND chmod a+rx "${CMAKE_BINARY_DIR}/launch-c" "${CMAKE_BINARY_DIR}/launch-cxx")
+
+ if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+ # ccache splits up the compile steps, so we end up with unused arguments in some steps.
+ # Clang also thinks that ccache isn't interactive, so we explicitly need to enable color.
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -fcolor-diagnostics")
+ endif()
+else()
+ message(STATUS "Can't find ccache — consider installing ccache to improve recompilation performance")
+endif()
+
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/platform/${MBGL_PLATFORM}/config.cmake)
message(ERROR "Can't find config.cmake file for platform ${MBGL_PLATFORM}")
endif()
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..88127011cc 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 "$@"
@@ -129,6 +110,14 @@ run-benchmark: run-benchmark-.
run-benchmark-%: benchmark
$(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/mbgl-benchmark --benchmark_filter=$*
+.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)
set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'mbgl-glfw' build $(XCPRETTY)
@@ -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)
@@ -474,21 +460,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 +478,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,24 +521,28 @@ 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
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
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
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="$$*"
@@ -577,37 +556,36 @@ 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
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all assemble$(BUILDTYPE)
@@ -617,29 +595,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 +664,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..06de2371b0 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,12 @@ 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:
@@ -25,3 +25,6 @@ Additional Mapbox GL Native–based libraries for **hybrid applications** are de
| [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/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/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 6bced990de..fa52c643cd 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,31 +1,163 @@
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-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
+
+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
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 +168,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 +197,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 +219,34 @@ 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
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 +258,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: .
@@ -133,4 +273,297 @@ jobs:
- deploy:
name: Publish to Maven
command: |
- if [ "${CIRCLE_BRANCH}" == "release-ios-v3.6.0-android-v5.1.0" ]; then make run-android-upload-archives ; fi
+ 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
+ - *publish-node-package
+ - *upload-render-tests
+
+# ------------------------------------------------------------------------------
+ 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
+ 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
+ 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
+ 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
+ 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..f17d95941d 100644
--- a/cmake/benchmark-files.cmake
+++ b/cmake/benchmark-files.cmake
@@ -3,18 +3,22 @@
set(MBGL_BENCHMARK_FILES
# api
benchmark/api/query.benchmark.cpp
+ benchmark/api/render.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
+
+ # 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..1a86da6f24 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,19 +99,15 @@ 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
@@ -155,91 +150,108 @@ 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_style.cpp
+ src/mbgl/renderer/render_style.hpp
+ src/mbgl/renderer/render_style_observer.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 +296,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 +311,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 +335,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 +352,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 +385,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 +452,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 +482,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
@@ -494,6 +518,8 @@ set(MBGL_CORE_FILES
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 +541,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
@@ -549,7 +576,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 +591,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
@@ -583,8 +611,6 @@ set(MBGL_CORE_FILES
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 +623,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/src/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp
index cf071d4933..406de425d4 100644
--- a/src/mbgl/actor/message.hpp
+++ b/include/mbgl/actor/message.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include <mbgl/util/optional.hpp>
+
+#include <future>
#include <utility>
namespace mbgl {
@@ -36,6 +39,31 @@ public:
ArgsTuple argsTuple;
};
+template <class ResultType, class Object, class MemberFn, class ArgsTuple>
+class AskMessageImpl : public Message {
+public:
+ AskMessageImpl(std::promise<ResultType> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
+ : object(object_),
+ memberFn(memberFn_),
+ argsTuple(std::move(argsTuple_)),
+ promise(std::move(promise_)) {
+ }
+
+ void operator()() override {
+ promise.set_value(ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()));
+ }
+
+ template <std::size_t... I>
+ ResultType ask(std::index_sequence<I...>) {
+ return (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
+ }
+
+ Object& object;
+ MemberFn memberFn;
+ ArgsTuple argsTuple;
+ std::promise<ResultType> promise;
+};
+
namespace actor {
template <class Object, class MemberFn, class... Args>
@@ -44,5 +72,11 @@ std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&..
return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple));
}
+template <class ResultType, class Object, class MemberFn, class... Args>
+std::unique_ptr<Message> makeMessage(std::promise<ResultType>&& promise, Object& object, MemberFn memberFn, Args&&... args) {
+ auto tuple = std::make_tuple(std::forward<Args>(args)...);
+ return std::make_unique<AskMessageImpl<ResultType, Object, MemberFn, decltype(tuple)>>(std::move(promise), object, memberFn, std::move(tuple));
+}
+
} // namespace actor
} // namespace mbgl
diff --git a/include/mbgl/actor/scheduler.hpp b/include/mbgl/actor/scheduler.hpp
index 83689c3348..d8a26ebeab 100644
--- a/include/mbgl/actor/scheduler.hpp
+++ b/include/mbgl/actor/scheduler.hpp
@@ -21,18 +21,21 @@ class Mailbox;
Subject to these constraints, processing can happen on whatever thread in the
pool is available.
- * `RunLoop` is a `Scheduler` that is typically used to create a mailbox and
- `ActorRef` for an object that lives on the main thread and is not itself wrapped
- as an `Actor`:
-
- auto mailbox = std::make_shared<Mailbox>(*util::RunLoop::Get());
+ * `Scheduler::GetCurrent()` is typically used to create a mailbox and `ActorRef`
+ for an object that lives on the main thread and is not itself wrapped an
+ `Actor`. The underlying implementation of this Scheduler should usually be
+ a `RunLoop`
+ auto mailbox = std::make_shared<Mailbox>(*Scheduler::Get());
Actor<Worker> worker(threadPool, ActorRef<Foo>(*this, mailbox));
*/
-
class Scheduler {
public:
virtual ~Scheduler() = default;
virtual void schedule(std::weak_ptr<Mailbox>) = 0;
+
+ // Set/Get the current Scheduler for this thread
+ static Scheduler* GetCurrent();
+ static void SetCurrent(Scheduler*);
};
} // namespace mbgl
diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp
index 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..7d6678dc93 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,40 @@
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 callback);
// 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 +133,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 +141,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..95828a1b79
--- /dev/null
+++ b/include/mbgl/renderer/renderer.hpp
@@ -0,0 +1,53 @@
+#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 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..f7d19a1791 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,26 @@ 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;
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 +62,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 +71,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 +83,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..9911e0ce67 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;
@@ -140,10 +145,14 @@ public:
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/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp
index 51cfc5a2a1..ffd75662e6 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,9 +20,7 @@ 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;
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..9252297d75 100644
--- a/include/mbgl/style/conversion/make_property_setters.hpp
+++ b/include/mbgl/style/conversion/make_property_setters.hpp
@@ -20,50 +20,51 @@ 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-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, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
+ result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
+ result["text-letter-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
+ result["text-justify"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
+ result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextAnchorType>, &SymbolLayer::setTextAnchor>;
+ result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
+ result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
+ result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
+ result["text-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
+ result["text-transform"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
+ result["text-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
+ result["text-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
+ result["text-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
+ result["text-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
@@ -74,129 +75,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..79a353b047 100644
--- a/include/mbgl/style/layers/custom_layer.hpp
+++ b/include/mbgl/style/layers/custom_layer.hpp
@@ -55,12 +55,21 @@ public:
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..6e355c0057 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,10 @@ public:
DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const;
void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>);
+ static PropertyValue<AlignmentType> getDefaultIconPitchAlignment();
+ PropertyValue<AlignmentType> getIconPitchAlignment() const;
+ void setIconPitchAlignment(PropertyValue<AlignmentType>);
+
static PropertyValue<AlignmentType> getDefaultTextPitchAlignment();
PropertyValue<AlignmentType> getTextPitchAlignment() const;
void setTextPitchAlignment(PropertyValue<AlignmentType>);
@@ -123,13 +134,13 @@ public:
PropertyValue<float> getTextLetterSpacing() const;
void setTextLetterSpacing(PropertyValue<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<TextAnchorType> getDefaultTextAnchor();
+ DataDrivenPropertyValue<TextAnchorType> getTextAnchor() const;
+ void setTextAnchor(DataDrivenPropertyValue<TextAnchorType>);
static PropertyValue<float> getDefaultTextMaxAngle();
PropertyValue<float> getTextMaxAngle() const;
@@ -170,101 +181,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..44b16f16e7 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 {
diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp
index dbc4bddde5..eb5c201793 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 };
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..cb28f3da8d 100644
--- a/include/mbgl/util/image.hpp
+++ b/include/mbgl/util/image.hpp
@@ -66,9 +66,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 +78,27 @@ 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));
+ }
+
// 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/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/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 f13860935a183bfaa9ef28bb88946b5843c2faa
diff --git a/package.json b/package.json
index 68d6b93859..2f665e680a 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,17 @@
},
"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"
},
"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",
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 6d1422e234..75cce3607c 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,6 +2,12 @@
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.2.0 - TBA
+
+* 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.3 - August 18, 2017
* Use separate attribute component for line normals [#9753](https://github.com/mapbox/mapbox-gl-native/pull/9753)
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..025097e756 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,10 +119,15 @@ android {
}
lintOptions {
+ baseline file("lint-baseline-local.xml")
checkAllWarnings true
warningsAsErrors true
}
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
+
buildTypes {
debug {
jniDebuggable true
@@ -145,3 +155,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 033903822f..db0c9047e3 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -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/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/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/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
deleted file mode 100644
index 9c8cda5544..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-package com.mapbox.mapboxsdk.http;
-
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.os.Build;
-import android.text.TextUtils;
-
-import com.mapbox.mapboxsdk.BuildConfig;
-import com.mapbox.mapboxsdk.Mapbox;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.NoRouteToHostException;
-import java.net.ProtocolException;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.net.ssl.SSLException;
-
-import okhttp3.Call;
-import okhttp3.Callback;
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.internal.Util;
-import timber.log.Timber;
-
-class HTTPRequest implements Callback {
-
- private static OkHttpClient mClient = new OkHttpClient();
- private String USER_AGENT_STRING = null;
-
- private static final int CONNECTION_ERROR = 0;
- private static final int TEMPORARY_ERROR = 1;
- private static final int PERMANENT_ERROR = 2;
-
- // Reentrancy is not needed, but "Lock" is an
- // abstract class.
- private ReentrantLock mLock = new ReentrantLock();
-
- private long mNativePtr = 0;
-
- private Call mCall;
- private Request mRequest;
-
- private native void nativeOnFailure(int type, String message);
-
- private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires,
- String retryAfter, String xRateLimitReset, byte[] body);
-
- private HTTPRequest(long nativePtr, String resourceUrl, String etag, String modified) {
- mNativePtr = nativePtr;
-
- try {
- // Don't try a request if we aren't connected
- if (!Mapbox.isConnected()) {
- throw new NoRouteToHostException("No Internet connection available.");
- }
-
- HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
- final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
- if (host.equals("mapbox.com") || host.endsWith(".mapbox.com") || host.equals("mapbox.cn")
- || host.endsWith(".mapbox.cn")) {
- if (httpUrl.querySize() == 0) {
- resourceUrl = resourceUrl + "?";
- } else {
- resourceUrl = resourceUrl + "&";
- }
- resourceUrl = resourceUrl + "events=true";
- }
-
- Request.Builder builder = new Request.Builder()
- .url(resourceUrl)
- .tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE))
- .addHeader("User-Agent", getUserAgent());
- if (etag.length() > 0) {
- builder = builder.addHeader("If-None-Match", etag);
- } else if (modified.length() > 0) {
- builder = builder.addHeader("If-Modified-Since", modified);
- }
- mRequest = builder.build();
- mCall = mClient.newCall(mRequest);
- mCall.enqueue(this);
- } catch (Exception exception) {
- onFailure(exception);
- }
- }
-
- public void cancel() {
- // mCall can be null if the constructor gets aborted (e.g, under a NoRouteToHostException).
- if (mCall != null) {
- mCall.cancel();
- }
-
- // TODO: We need a lock here because we can try
- // to cancel at the same time the request is getting
- // answered on the OkHTTP thread. We could get rid of
- // this lock by using Runnable when we move Android
- // implementation of mbgl::RunLoop to Looper.
- mLock.lock();
- mNativePtr = 0;
- mLock.unlock();
- }
-
- @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()));
- } 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));
- }
-
- byte[] body;
- try {
- body = response.body().bytes();
- } catch (IOException ioException) {
- onFailure(ioException);
- // throw ioException;
- return;
- } finally {
- response.body().close();
- }
-
- mLock.lock();
- if (mNativePtr != 0) {
- nativeOnResponse(response.code(),
- response.header("ETag"),
- response.header("Last-Modified"),
- response.header("Cache-Control"),
- response.header("Expires"),
- response.header("Retry-After"),
- response.header("x-rate-limit-reset"),
- body);
- }
- mLock.unlock();
- }
-
- @Override
- public void onFailure(Call call, IOException e) {
- onFailure(e);
- }
-
- private void onFailure(Exception e) {
- int type = PERMANENT_ERROR;
- if ((e instanceof NoRouteToHostException) || (e instanceof UnknownHostException) || (e instanceof SocketException)
- || (e instanceof ProtocolException) || (e instanceof SSLException)) {
- type = CONNECTION_ERROR;
- } else if ((e instanceof InterruptedIOException)) {
- type = TEMPORARY_ERROR;
- }
-
- 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));
- } else if (type == CONNECTION_ERROR) {
- Timber.i(String.format(MapboxConstants.MAPBOX_LOCALE,
- "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));
- }
-
- mLock.lock();
- if (mNativePtr != 0) {
- nativeOnFailure(type, errorMessage);
- }
- mLock.unlock();
- }
-
- private String getUserAgent() {
- if (USER_AGENT_STRING == null) {
- return USER_AGENT_STRING = Util.toHumanReadableAscii(
- String.format("%s %s (%s) Android/%s (%s)",
- getApplicationIdentifier(),
- BuildConfig.MAPBOX_VERSION_STRING,
- BuildConfig.GIT_REVISION_SHORT,
- Build.VERSION.SDK_INT,
- Build.CPU_ABI)
- );
- } else {
- return USER_AGENT_STRING;
- }
- }
-
- private String getApplicationIdentifier() {
- try {
- Context context = Mapbox.getApplicationContext();
- PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
- return String.format("%s/%s (%s)", context.getPackageName(), packageInfo.versionName, packageInfo.versionCode);
- } catch (Exception exception) {
- return "";
- }
- }
-}
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
new file mode 100644
index 0000000000..4f899cf696
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequest.java
@@ -0,0 +1,238 @@
+package com.mapbox.mapboxsdk.http;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+
+import com.mapbox.mapboxsdk.BuildConfig;
+import com.mapbox.mapboxsdk.Mapbox;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.NoRouteToHostException;
+import java.net.ProtocolException;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.net.ssl.SSLException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.internal.Util;
+import timber.log.Timber;
+
+/**
+ * Gateway to request and return HttpRequest to c++
+ * <p>
+ * Reentrancy lock is not needed, but "Lock" is an abstract class.
+ * </p>
+ */
+class HttpRequest implements Callback {
+
+ @IntDef( {CONNECTION_ERROR, TEMPORARY_ERROR, PERMANENT_ERROR})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HttpError {
+ }
+
+ private static final int CONNECTION_ERROR = 0;
+ private static final int TEMPORARY_ERROR = 1;
+ private static final int PERMANENT_ERROR = 2;
+
+ private static final ReentrantLock LOCK = new ReentrantLock();
+ private static final OkHttpClient HTTP_CLIENT = new OkHttpClient();
+
+ private static boolean logEnabled = true;
+ private static String userAgentString;
+
+ private long nativePtr;
+ private Call call;
+
+ private native void nativeOnFailure(@HttpError int type, String message);
+
+ private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires,
+ String retryAfter, String xRateLimitReset, byte[] body);
+
+ private HttpRequest(long nativePtr, String resourceUrl, String etag, String modified) {
+ this.nativePtr = nativePtr;
+
+ try {
+ // Don't try a request if we aren't connected
+ if (!Mapbox.isConnected()) {
+ throw new NoRouteToHostException("No Internet connection available.");
+ }
+
+ resourceUrl = adaptResourceUrl(resourceUrl);
+ call = HTTP_CLIENT.newCall(buildRequest(resourceUrl, etag, modified));
+ call.enqueue(this);
+ } catch (Exception exception) {
+ onFailure(exception);
+ }
+ }
+
+ private String adaptResourceUrl(String resourceUrl) {
+ HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
+ if (httpUrl != null && isMapboxHost(httpUrl)) {
+ HttpUrl.Builder builder = httpUrl.newBuilder();
+ builder.addQueryParameter("events", "true");
+ resourceUrl = builder.build().toString();
+ }
+ return resourceUrl;
+ }
+
+ private boolean isMapboxHost(@NonNull HttpUrl httpUrl) {
+ final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
+ return host.equals("mapbox.com") || host.endsWith(".mapbox.com")
+ || host.equals("mapbox.cn") || host.endsWith(".mapbox.cn");
+ }
+
+ private Request buildRequest(String resourceUrl, String etag, String modified) {
+ Request.Builder builder = new Request.Builder()
+ .url(resourceUrl)
+ .tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE))
+ .addHeader("User-Agent", getUserAgentString());
+ if (etag.length() > 0) {
+ builder = builder.addHeader("If-None-Match", etag);
+ } else if (modified.length() > 0) {
+ builder = builder.addHeader("If-Modified-Since", modified);
+ }
+ return builder.build();
+ }
+
+ public void cancel() {
+ // call can be null if the constructor gets aborted (e.g, under a NoRouteToHostException).
+ if (call != null) {
+ call.cancel();
+ }
+
+ // TODO: We need a LOCK here because we can try
+ // to cancel at the same time the request is getting
+ // answered on the OkHTTP thread. We could get rid of
+ // this LOCK by using Runnable when we move Android
+ // implementation of mbgl::RunLoop to Looper.
+ LOCK.lock();
+ nativePtr = 0;
+ LOCK.unlock();
+ }
+
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
+ if (logEnabled) {
+ logOnResponse(response);
+ }
+
+ byte[] body;
+ try {
+ body = response.body().bytes();
+ } catch (IOException ioException) {
+ onFailure(ioException);
+ return;
+ } finally {
+ response.body().close();
+ }
+
+ LOCK.lock();
+ if (nativePtr != 0) {
+ nativeOnResponse(response.code(),
+ response.header("ETag"),
+ response.header("Last-Modified"),
+ response.header("Cache-Control"),
+ response.header("Expires"),
+ response.header("Retry-After"),
+ response.header("x-rate-limit-reset"),
+ body);
+ }
+ LOCK.unlock();
+ }
+
+ private void logOnResponse(Response response) {
+ if (response.isSuccessful()) {
+ Timber.v("[HTTP] Request was successful (code = %s).", response.code());
+ } else if (logEnabled) {
+ // 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("[HTTP] Request with response code = %s: %s", response.code(), message);
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull IOException exception) {
+ onFailure(exception);
+ }
+
+ private void onFailure(Exception exception) {
+ int failType = resolveFailureType(exception);
+ String errorMessage = exception.getMessage() != null ? exception.getMessage() : "Error processing the request";
+ if (logEnabled) {
+ logOnFailure(failType, errorMessage);
+ }
+
+ LOCK.lock();
+ if (nativePtr != 0) {
+ nativeOnFailure(failType, errorMessage);
+ }
+ LOCK.unlock();
+ }
+
+ @HttpError
+ private int resolveFailureType(Exception exception) {
+ int type = PERMANENT_ERROR;
+ if ((exception instanceof NoRouteToHostException) || (exception instanceof UnknownHostException)
+ || (exception instanceof SocketException) || (exception instanceof ProtocolException)
+ || (exception instanceof SSLException)) {
+ type = CONNECTION_ERROR;
+ } else if ((exception instanceof InterruptedIOException)) {
+ type = TEMPORARY_ERROR;
+ }
+ return type;
+ }
+
+ private void logOnFailure(@HttpError int type, String errorMessage) {
+ if (type == TEMPORARY_ERROR) {
+ Timber.d("Request failed due to a temporary error: %s", errorMessage);
+ } else if (type == CONNECTION_ERROR) {
+ Timber.i("Request failed due to a connection error: %s", errorMessage);
+ } else {
+ // PERMANENT_ERROR
+ Timber.w("Request failed due to a permanent error: %s", errorMessage);
+ }
+ }
+
+ private String getUserAgentString() {
+ if (userAgentString == null) {
+ userAgentString = Util.toHumanReadableAscii(
+ String.format("%s %s (%s) Android/%s (%s)",
+ getApplicationIdentifier(),
+ BuildConfig.MAPBOX_VERSION_STRING,
+ BuildConfig.GIT_REVISION_SHORT,
+ Build.VERSION.SDK_INT,
+ Build.CPU_ABI)
+ );
+ }
+ return userAgentString;
+ }
+
+ private String getApplicationIdentifier() {
+ try {
+ Context context = Mapbox.getApplicationContext();
+ PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+ return String.format("%s/%s (%s)", context.getPackageName(), packageInfo.versionName, packageInfo.versionCode);
+ } catch (Exception exception) {
+ return "";
+ }
+ }
+
+ static void enableLog(boolean enabled) {
+ logEnabled = enabled;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java
new file mode 100644
index 0000000000..46359572e6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java
@@ -0,0 +1,19 @@
+package com.mapbox.mapboxsdk.http;
+
+/**
+ * Utility class for setting HttpRequest configurations
+ */
+public class HttpRequestUtil {
+
+ /**
+ * Set the log state of HttpRequest.
+ * <p>
+ * This configuration will outlast the lifecycle of the Map.
+ * </p>
+ *
+ * @param enabled True will enable logging, false will disable
+ */
+ public static void setLogEnabled(boolean enabled) {
+ HttpRequest.enableLog(enabled);
+ }
+}
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/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/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 d2973bf558..f456d3ef65 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
@@ -50,13 +50,12 @@ final class MapGestureDetector {
private PointF focalPoint;
- private boolean twoTap = false;
- private boolean zoomStarted = false;
- private boolean dragStarted = false;
- private boolean quickZoom = false;
- private boolean scrollInProgress = false;
- private boolean scaleGestureOccurred = false;
- private boolean recentScaleGestureOccurred = false;
+ private boolean twoTap;
+ private boolean quickZoom;
+ private boolean tiltGestureOccurred;
+ private boolean scrollGestureOccurred;
+ private boolean scaleGestureOccurred;
+ private boolean recentScaleGestureOccurred;
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
TrackingSettings trackingSettings, AnnotationManager annotationManager,
@@ -193,10 +192,10 @@ final class MapGestureDetector {
}
// Scroll / Pan Has Stopped
- if (scrollInProgress) {
+ if (scrollGestureOccurred) {
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent(
getLocationFromGesture(event.getX(), event.getY()), transform));
- scrollInProgress = false;
+ scrollGestureOccurred = false;
cameraChangeDispatcher.onCameraIdle();
}
@@ -393,20 +392,18 @@ final class MapGestureDetector {
return false;
}
- if (dragStarted) {
+ if (tiltGestureOccurred) {
return false;
}
- if (scaleGestureOccurred) {
- return false;
- }
-
- if (!scrollInProgress) {
- scrollInProgress = true;
+ if (!scrollGestureOccurred) {
+ scrollGestureOccurred = true;
// Cancel any animation
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ if (!scaleGestureOccurred) {
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ }
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
getLocationFromGesture(e1.getX(), e1.getY()),
@@ -456,7 +453,6 @@ final class MapGestureDetector {
scaleGestureOccurred = false;
beginTime = 0;
scaleFactor = 1.0f;
- zoomStarted = false;
cameraChangeDispatcher.onCameraIdle();
}
@@ -468,27 +464,27 @@ final class MapGestureDetector {
return super.onScale(detector);
}
- // If scale is large enough ignore a tap
- scaleFactor *= detector.getScaleFactor();
- if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) {
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- zoomStarted = true;
+ if (tiltGestureOccurred) {
+ return false;
}
// Ignore short touches in case it is a tap
// Also ignore small scales
long time = detector.getEventTime();
long interval = time - beginTime;
- if (!zoomStarted && (interval <= ViewConfiguration.getTapTimeout())) {
+ if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
return false;
}
- if (!zoomStarted) {
- return false;
+ // If scale is large enough ignore a tap
+ scaleFactor *= detector.getScaleFactor();
+ if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) {
+ // notify camera change listener
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ scaleGestureOccurred = true;
}
- if (dragStarted) {
+ if (!scaleGestureOccurred) {
return false;
}
@@ -506,7 +502,7 @@ final class MapGestureDetector {
// Scale the map
if (focalPoint != null) {
// arround user provided focal point
- transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(2), focalPoint.x, focalPoint.y);
+ transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y);
} else if (quickZoom) {
cameraChangeDispatcher.onCameraMove();
// clamp scale factors we feed to core #7514
@@ -514,10 +510,12 @@ final class MapGestureDetector {
MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP,
MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP);
// around center map
- transform.zoomBy(Math.log(scaleFactor) / Math.log(2), uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ transform.zoomBy(Math.log(scaleFactor) / Math.log(Math.PI / 2),
+ uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
} else {
// around gesture
- transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(2), detector.getFocusX(), detector.getFocusY());
+ transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2),
+ detector.getFocusX(), detector.getFocusY());
}
return true;
@@ -544,9 +542,6 @@ final class MapGestureDetector {
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
beginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_ROTATION_START, transform));
return true;
}
@@ -563,17 +558,10 @@ final class MapGestureDetector {
// Called for rotation
@Override
public boolean onRotate(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled() || dragStarted) {
+ if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) {
return false;
}
- // If rotate is large enough ignore a tap
- // Also is zoom already started, don't rotate
- totalAngle += detector.getRotationDegreesDelta();
- if (totalAngle > 20.0f || totalAngle < -20.0f) {
- started = true;
- }
-
// Ignore short touches in case it is a tap
// Also ignore small rotate
long time = detector.getEventTime();
@@ -582,6 +570,16 @@ final class MapGestureDetector {
return false;
}
+ // If rotate is large enough ignore a tap
+ // Also is zoom already started, don't rotate
+ totalAngle += detector.getRotationDegreesDelta();
+ if (totalAngle > 35.0f || totalAngle < -35.0f) {
+ MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
+ getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
+ MapboxEvent.GESTURE_ROTATION_START, transform));
+ started = true;
+ }
+
if (!started) {
return false;
}
@@ -610,9 +608,8 @@ final class MapGestureDetector {
*/
private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener {
- long beginTime = 0;
- float totalDelta = 0.0f;
- boolean started = false;
+ private long beginTime = 0;
+ private float totalDelta = 0.0f;
@Override
public boolean onShoveBegin(ShoveGestureDetector detector) {
@@ -622,10 +619,6 @@ final class MapGestureDetector {
// notify camera change listener
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- beginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_PITCH_START, transform));
return true;
}
@@ -633,8 +626,7 @@ final class MapGestureDetector {
public void onShoveEnd(ShoveGestureDetector detector) {
beginTime = 0;
totalDelta = 0.0f;
- started = false;
- dragStarted = false;
+ tiltGestureOccurred = false;
}
@Override
@@ -643,22 +635,26 @@ final class MapGestureDetector {
return false;
}
- // If tilt is large enough ignore a tap
- // Also if zoom already started, don't tilt
- totalDelta += detector.getShovePixelsDelta();
- if (!zoomStarted && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) {
- started = true;
- }
-
// Ignore short touches in case it is a tap
// Also ignore small tilt
long time = detector.getEventTime();
long interval = time - beginTime;
- if (!started && (interval <= ViewConfiguration.getTapTimeout())) {
+ if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
return false;
}
- if (!started) {
+ // If tilt is large enough ignore a tap
+ // Also if zoom already started, don't tilt
+ totalDelta += detector.getShovePixelsDelta();
+ if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) {
+ tiltGestureOccurred = true;
+ beginTime = detector.getEventTime();
+ MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
+ getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
+ MapboxEvent.GESTURE_PITCH_START, transform));
+ }
+
+ if (!tiltGestureOccurred) {
return false;
}
@@ -669,9 +665,6 @@ final class MapGestureDetector {
// Tilt the map
transform.setTilt(pitch);
-
- dragStarted = true;
-
return true;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index 32c4952aa1..cfe042d1f9 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
@@ -4,13 +4,14 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.SurfaceTexture;
+import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.CallSuper;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
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;
@@ -21,11 +22,13 @@ 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;
@@ -40,6 +43,9 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import timber.log.Timber;
/**
* <p>
@@ -57,17 +63,24 @@ 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 ImageView attrView;
+ private ImageView logoView;
private MapGestureDetector mapGestureDetector;
private MapKeyListener mapKeyListener;
private MapZoomButtonController mapZoomButtonController;
+ private Bundle savedInstanceState;
+ private final CopyOnWriteArrayList<OnMapChangedListener> onMapChangedListeners = new CopyOnWriteArrayList<>();
@UiThread
public MapView(@NonNull Context context) {
@@ -98,21 +111,36 @@ 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);
+ mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this));
// 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(mapboxMapOptions);
+ }
+ });
+ }
- // 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);
@@ -128,23 +156,31 @@ public class MapView extends FrameLayout {
// setup components for MapboxMap creation
Projection proj = new Projection(nativeMapView);
- UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView));
+ UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, logoView);
TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator);
MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint);
+ 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);
+ 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);
MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform);
- mapZoomButtonController = new MapZoomButtonController(this, uiSettings, zoomListener);
+ mapZoomButtonController.bind(uiSettings, zoomListener);
// inject widgets with MapboxMap
compassView.setMapboxMap(mapboxMap);
@@ -158,14 +194,15 @@ 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);
+ }
}
//
@@ -188,22 +225,18 @@ 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) {
+ private void initialiseDrawingSurface(MapboxMapOptions mapboxMapOptions) {
+ if (mapboxMapOptions.getTextureMode()) {
TextureView textureView = new TextureView(getContext());
textureView.setSurfaceTextureListener(new SurfaceTextureListener());
addView(textureView, 0);
} else {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
+ surfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
surfaceView.getHolder().addCallback(new SurfaceCallback());
surfaceView.setVisibility(View.VISIBLE);
}
@@ -226,8 +259,10 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onStart() {
- mapboxMap.onStart();
ConnectivityReceiver.instance(getContext()).activate();
+ if (mapboxMap != null) {
+ mapboxMap.onStart();
+ }
}
/**
@@ -261,9 +296,6 @@ public class MapView extends FrameLayout {
@UiThread
public void onDestroy() {
destroyed = true;
- nativeMapView.terminateContext();
- nativeMapView.terminateDisplay();
- nativeMapView.destroySurface();
nativeMapView.destroy();
nativeMapView = null;
}
@@ -374,7 +406,10 @@ public class MapView extends FrameLayout {
if (destroyed) {
return;
}
-
+ if (nativeMapView == null) {
+ mapboxMapOptions.styleUrl(url);
+ return;
+ }
nativeMapView.setStyleUrl(url);
}
@@ -402,7 +437,6 @@ public class MapView extends FrameLayout {
if (!hasSurface) {
return;
}
-
nativeMapView.render();
}
@@ -412,7 +446,7 @@ public class MapView extends FrameLayout {
return;
}
- if (!isInEditMode()) {
+ if (!isInEditMode() && nativeMapView != null) {
nativeMapView.resizeView(width, height);
}
}
@@ -423,7 +457,16 @@ public class MapView extends FrameLayout {
@Override
public void surfaceCreated(SurfaceHolder holder) {
- nativeMapView.createSurface(surface = holder.getSurface());
+ if (nativeMapView == null) {
+ nativeMapView = new NativeMapView(MapView.this);
+ nativeMapView.createSurface(surface = holder.getSurface());
+ nativeMapView.resizeView(getWidth(), getHeight());
+ initialiseMap();
+ mapboxMap.onStart();
+ } else {
+ nativeMapView.createSurface(surface = holder.getSurface());
+ }
+
hasSurface = true;
}
@@ -440,6 +483,7 @@ public class MapView extends FrameLayout {
hasSurface = false;
if (nativeMapView != null) {
+ // occurs when activity goes to background
nativeMapView.destroySurface();
}
surface.release();
@@ -455,8 +499,17 @@ public class MapView extends FrameLayout {
// 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);
+ if (nativeMapView == null) {
+ nativeMapView = new NativeMapView(MapView.this);
+ nativeMapView.createSurface(this.surface = new Surface(surface));
+ nativeMapView.resizeFramebuffer(width, height);
+ nativeMapView.resizeView(width, height);
+ initialiseMap();
+ mapboxMap.onStart();
+ } else {
+ nativeMapView.createSurface(this.surface = new Surface(surface));
+ }
+
hasSurface = true;
}
@@ -504,24 +557,35 @@ 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 (nativeMapView != 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>
* Add a callback that's invoked when the displayed map view changes.
@@ -533,7 +597,7 @@ public class MapView extends FrameLayout {
*/
public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
- nativeMapView.addOnMapChangedListener(listener);
+ onMapChangedListeners.add(listener);
}
}
@@ -545,7 +609,7 @@ public class MapView extends FrameLayout {
*/
public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
- nativeMapView.removeOnMapChangedListener(listener);
+ onMapChangedListeners.remove(listener);
}
}
@@ -909,11 +973,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;
}
@@ -921,14 +985,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/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
index 80b25bf0de..94e2fbcf3b 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,6 +79,9 @@ 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;
@@ -148,10 +151,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 +297,14 @@ public class MapboxMapOptions implements Parcelable {
mapboxMapOptions.myLocationAccuracyTint(
typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor,
ColorUtils.getPrimaryColor(context)));
+ mapboxMapOptions.myLocationAccuracyThreshold(
+ typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0));
mapboxMapOptions.textureMode(
typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false));
+ mapboxMapOptions.setPrefetchesTiles(
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true));
+ mapboxMapOptions.renderSurfaceOnTop(
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableZMediaOverlay, false));
} finally {
typedArray.recycle();
}
@@ -681,6 +693,17 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * 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 TextureView as rendered surface.
* <p>
* Since the 4.2.0 release we replaced our TextureView with an SurfaceView implemenation.
@@ -697,6 +720,48 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render
+ * a low resolution preview while more detailed tiles are loaded.
+ * Enabled by default
+ *
+ * @param enable true to enable
+ *
+ * @return This
+ */
+ public MapboxMapOptions setPrefetchesTiles(boolean enable) {
+ this.prefetchesTiles = enable;
+ return this;
+ }
+
+ /**
+ * 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,6 +1053,15 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * Returns current accuracy threshold value (in meters).
+ *
+ * @return Value of accuracy threshold (in meters), below which circle won't be displayed
+ */
+ public float getMyLocationAccuracyThreshold() {
+ return myLocationAccuracyThreshold;
+ }
+
+ /**
* Get the current configured debug state for a map view.
*
* @return True indicates debug is enabled.
@@ -1065,10 +1139,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 +1230,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 +1269,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 +1317,13 @@ 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..4f5037e4b3 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,11 +1,9 @@
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;
@@ -13,12 +11,12 @@ 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;
@@ -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;
@@ -58,18 +55,11 @@ final class NativeMapView {
// 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();
}
//
@@ -81,27 +71,10 @@ final class NativeMapView {
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, pixelRatio, programCacheDir);
}
//
@@ -110,9 +83,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,34 +97,6 @@ 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;
@@ -197,14 +143,14 @@ 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);
@@ -561,6 +507,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;
@@ -621,7 +574,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 +645,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 +808,13 @@ final class NativeMapView {
nativeRemoveImage(name);
}
+ public Bitmap getImage(String name) {
+ if (isDestroyedOn("getImage")) {
+ return null;
+ }
+ return nativeGetImage(name);
+ }
+
// Feature querying
@NonNull
@@ -908,14 +882,8 @@ final class NativeMapView {
}
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());
- }
- }
+ if (mapView != null) {
+ mapView.onMapChange(rawChange);
}
}
@@ -944,9 +912,7 @@ final class NativeMapView {
private native void nativeInitialize(NativeMapView nativeMapView,
FileSource fileSource,
float pixelRatio,
- String programCacheDir,
- int availableProcessors,
- long totalMemory);
+ String programCacheDir);
private native void nativeDestroy();
@@ -1038,6 +1004,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);
@@ -1117,6 +1085,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 +1104,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 +1127,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);
}
//
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/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 1c78d5979e..1b8c4121ef 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/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/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
index 5e345268f9..be24b65d27 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,32 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface ICON_TEXT_FIT {}
+ // 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 +472,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..ef89c6809e 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,29 @@ public class PropertyFactory {
}
/**
+ * 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
@@ -2058,11 +2103,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 +2126,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..fe81dcb638 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,16 @@ public class SymbolLayer extends Layer {
}
/**
+ * 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 +901,8 @@ public class SymbolLayer extends Layer {
private native Object nativeGetIconOffset();
+ 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/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
index f30cb7c27a..d59eef7427 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
@@ -75,16 +75,21 @@
<!-- 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_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..281fe8afe3 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"/>
@@ -116,6 +117,9 @@
<!-- Deprecated to use TextureView-->
<attr name="mapbox_renderTextureMode" format="boolean"/>
+ <attr name="mapbox_enableTilePrefetch" format="boolean"/>
+ <attr name="mapbox_enableZMediaOverlay" format="boolean"/>
+
</declare-styleable>
<declare-styleable name="mapbox_BubbleLayout">
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
index 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 8d9a360714..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
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..360cb36067 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -25,15 +25,10 @@ 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'
}
buildTypes {
@@ -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..e2694af348 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,54 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@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();
@@ -1840,6 +1888,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 +1993,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..3148ec6793 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
@@ -43,10 +43,10 @@ public class OnMapReadyIdlingResource implements IdlingResource {
Field field = activity.getClass().getDeclaredField("mapboxMap");
field.setAccessible(true);
mapboxMap = (MapboxMap) field.get(activity);
- Timber.e("isMapboxReady called with value " + (mapboxMap != null));
+ Timber.e("isMapboxReady called with value %s", (mapboxMap != null));
return mapboxMap != null;
} catch (Exception exception) {
- Timber.e("could not reflect", exception);
+ Timber.e(exception, "could not reflect");
return false;
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 1a70e4548a..5dc322a530 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -415,6 +415,28 @@
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"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
+ 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_maplayout"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
android:name=".activity.style.RuntimeStyleActivity"
android:description="@string/description_runtime_style"
android:label="@string/activity_runtime_style">
@@ -535,6 +557,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 +626,12 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
- android:name=".activity.annotation.AddRemoveMarkerActivity"
+ 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 +682,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
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..a8a5054eaf 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
@@ -53,7 +53,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/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
index 10464b087a..9498550f52 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;
@@ -83,6 +84,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/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..a909bb7151 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
@@ -53,7 +53,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
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/maplayout/BottomSheetActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
new file mode 100644
index 0000000000..1f016e430b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
@@ -0,0 +1,285 @@
+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;
+
+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..5b394a7895 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
@@ -41,10 +41,18 @@ public class DebugModeActivity extends AppCompatActivity {
setContentView(R.layout.activity_debug_mode);
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.e("New loaded style = %s", mapboxMap.getStyleJson());
+ }
+ }
+ });
+
mapView.setTag(true);
mapView.setStyleUrl(STYLES[currentStyleIndex]);
mapView.onCreate(savedInstanceState);
-
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull MapboxMap map) {
@@ -63,13 +71,12 @@ public class DebugModeActivity extends AppCompatActivity {
}
});
-
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();
}
}
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/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..3f0c2db81f 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,10 +12,10 @@ import android.widget.Toast;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.http.HttpRequestUtil;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
@@ -69,14 +69,14 @@ public class OfflineActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ HttpRequestUtil.setLogEnabled(false);
setContentView(R.layout.activity_offline);
// You can use Mapbox.setConnected(Boolean) to manually set the connectivity
// 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);
@@ -157,6 +157,7 @@ public class OfflineActivity extends AppCompatActivity
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
+ HttpRequestUtil.setLogEnabled(true);
}
@Override
@@ -207,7 +208,7 @@ public class OfflineActivity extends AppCompatActivity
@Override
public void onError(String error) {
- Timber.e("Error: " + error);
+ Timber.e("Error: %s" , error);
}
});
}
@@ -223,7 +224,7 @@ public class OfflineActivity extends AppCompatActivity
}
// Start progress bar
- Timber.d("Download started: " + regionName);
+ Timber.d("Download started: %s", regionName);
startProgress();
// Definition
@@ -241,14 +242,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/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/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/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
new file mode 100644
index 0000000000..ab33827e61
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
@@ -0,0 +1,123 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.style.functions.Function.zoom;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+
+public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
+
+ private static final String SOURCE_URL = "https://gist.githubusercontent.com/anonymous/"
+ + "70ddad0e58520307cd1699e704f4b626/raw/a5deaccdc4517c12d63b1f8abcb7becf5e36506a/map.geojson";
+ 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 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;
+ addSource();
+ addLayer();
+ }
+ });
+ }
+
+ private void addSource() {
+ try {
+ source = new GeoJsonSource(SOURCE_ID, new URL(SOURCE_URL));
+ } catch (MalformedURLException exception) {
+ Timber.e(exception, "That's not an url... ");
+ }
+ mapboxMap.addSource(source);
+ }
+
+ 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))
+ )
+ )
+ ),
+ iconAllowOverlap(true)
+ );
+ mapboxMap.addLayer(layer);
+ }
+
+ @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/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/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/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_add_remove_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml
deleted file mode 100644
index 6da5a61ecb..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- 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"/>
-
-</LinearLayout>
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..cfc923902c
--- /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="46.437"
+ app:mapbox_cameraTargetLng="-80.425"
+ app:mapbox_cameraZoom="3"
+ 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_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..0a98f7727f 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
@@ -21,6 +21,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="8dp"
+ android:textIsSelectable="false"
android:textSize="14sp"/>
<android.support.design.widget.FloatingActionButton
@@ -29,6 +30,7 @@
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
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"
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_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_visible_bounds.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_visibility.xml
index 89a28a7799..c33034181c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.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: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_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_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_double_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
index b976013ead..ecfa1372f6 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
@@ -9,7 +9,6 @@
android:layout_height="match_parent" />
<FrameLayout
- android:id="@+id/map_card"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="5dp"
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_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/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..0fbf9754c5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -13,7 +13,7 @@
<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_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>
@@ -53,12 +53,16 @@
<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_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>
<!--Description-->
<string name="description_user_location_tracking">Tracks the location of the user</string>
@@ -108,7 +112,9 @@
<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_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>
@@ -117,6 +123,8 @@
<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>
<!--Categories-->
<string name="category">category</string>
@@ -142,6 +150,8 @@
<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>
<!--Menu-->
<string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>
@@ -177,5 +187,57 @@
<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>
+ <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>
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/config.cmake b/platform/android/config.cmake
index 84591d644b..390e4842f4 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,10 @@ 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_frontend.cpp
+ PRIVATE platform/android/src/android_renderer_frontend.hpp
)
target_include_directories(mbgl-core
@@ -158,6 +163,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
@@ -222,6 +229,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 +308,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 4302fd816a..4585e0cb1f 100644
--- a/platform/android/dependencies.gradle
+++ b/platform/android/dependencies.gradle
@@ -9,7 +9,6 @@ ext {
mapboxServicesVersion = "2.2.1"
supportLibVersion = "25.3.1"
- wearableVersion = '2.0.0'
espressoVersion = '2.2.2'
testRunnerVersion = '0.5'
leakCanaryVersion = '1.5'
@@ -41,10 +40,6 @@ 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',
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/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_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp
new file mode 100644
index 0000000000..597bebe40d
--- /dev/null
+++ b/platform/android/src/android_renderer_frontend.cpp
@@ -0,0 +1,74 @@
+#include "android_renderer_frontend.hpp"
+
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer.hpp>
+
+namespace mbgl {
+namespace android {
+
+AndroidRendererFrontend::AndroidRendererFrontend(
+ std::unique_ptr<Renderer> renderer_,
+ RendererBackend& backend_,
+ InvalidateCallback invalidate)
+ : renderer(std::move(renderer_))
+ , backend(backend_)
+ , asyncInvalidate([=, invalidate=std::move(invalidate)]() {
+ invalidate();
+ }) {
+}
+
+AndroidRendererFrontend::~AndroidRendererFrontend() = default;
+
+void AndroidRendererFrontend::reset() {
+ assert (renderer);
+ if (renderer) {
+ renderer.reset();
+ }
+}
+
+void AndroidRendererFrontend::setObserver(RendererObserver& observer) {
+ assert (renderer);
+ renderer->setObserver(&observer);
+}
+
+void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) {
+ updateParameters = std::move(params);
+ asyncInvalidate.send();
+}
+
+void AndroidRendererFrontend::render() {
+ assert (renderer);
+ if (!updateParameters) return;
+
+ BackendScope guard { backend };
+
+ renderer->render(*updateParameters);
+}
+
+void AndroidRendererFrontend::onLowMemory() {
+ assert (renderer);
+ renderer->onLowMemory();
+}
+
+std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID,
+ const SourceQueryOptions& options) const {
+ return renderer->querySourceFeatures(sourceID, options);
+}
+
+std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box,
+ const RenderedQueryOptions& options) const {
+ return renderer->queryRenderedFeatures(box, options);
+}
+
+std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point,
+ const RenderedQueryOptions& options) const {
+ return renderer->queryRenderedFeatures(point, options);
+}
+
+AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& box) const {
+ return renderer->queryPointAnnotations(box);
+}
+
+} // 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..107bbbe0b6
--- /dev/null
+++ b/platform/android/src/android_renderer_frontend.hpp
@@ -0,0 +1,51 @@
+
+#pragma once
+
+#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/geo.hpp>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace mbgl {
+
+class Renderer;
+class RenderedQueryOptions;
+class SourceQueryOptions;
+
+namespace android {
+
+class AndroidRendererFrontend : public RendererFrontend {
+public:
+ using InvalidateCallback = std::function<void ()>;
+ AndroidRendererFrontend(std::unique_ptr<Renderer>, RendererBackend&, InvalidateCallback);
+ ~AndroidRendererFrontend() override;
+
+ void reset() override;
+ void setObserver(RendererObserver&) override;
+
+ void update(std::shared_ptr<UpdateParameters>) override;
+ void render();
+
+ // 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:
+ std::unique_ptr<Renderer> renderer;
+ RendererBackend& backend;
+ std::shared_ptr<UpdateParameters> updateParameters;
+ util::AsyncTask asyncInvalidate;
+};
+
+} // 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/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..1830923bbe 100644
--- a/platform/android/src/http_file_source.cpp
+++ b/platform/android/src/http_file_source.cpp
@@ -20,7 +20,7 @@ public:
class HTTPRequest : public AsyncRequest {
public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/http/HTTPRequest"; };
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/http/HttpRequest"; };
HTTPRequest(jni::JNIEnv&, const Resource&, FileSource::Callback);
~HTTPRequest();
@@ -61,7 +61,7 @@ void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
- jni::RegisterNativePeer<HTTPRequest>(env, HTTPRequest::javaClass, "mNativePtr",
+ jni::RegisterNativePeer<HTTPRequest>(env, HTTPRequest::javaClass, "nativePtr",
METHOD(&HTTPRequest::onFailure, "nativeOnFailure"),
METHOD(&HTTPRequest::onResponse, "nativeOnResponse"));
}
@@ -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..db8dd1dbdf 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"
@@ -127,6 +128,7 @@ void registerNatives(JavaVM *vm) {
// Geometry
LatLng::registerNative(env);
LatLngBounds::registerNative(env);
+ LatLngQuad::registerNative(env);
ProjectedMeters::registerNative(env);
// GSon
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 159ba70508..d859d929d3 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -14,7 +14,7 @@
#include <jni/jni.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/event.hpp>
@@ -25,8 +25,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/renderer.hpp>
// Java -> C++ conversion
#include "style/android_conversion.hpp"
@@ -41,12 +43,14 @@
#include "jni.hpp"
#include "attach_env.hpp"
+#include "android_renderer_frontend.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 {
@@ -55,13 +59,9 @@ 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)
+ jni::String _programCacheDir)
: javaPeer(_obj.NewWeakGlobalRef(_env)),
pixelRatio(_pixelRatio),
- availableProcessors(_availableProcessors),
- totalMemory(_totalMemory),
threadPool(sharedThreadPool()) {
// Get a reference to the JavaVM for callbacks
@@ -70,25 +70,28 @@ 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));
+ auto& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource);
- recalculateSourceTileCacheSize();
-}
+ // Create a renderer
+ auto renderer = std::make_unique<Renderer>(*this, pixelRatio, fileSource, *threadPool,
+ GLContextMode::Unique,
+ jni::Make<std::string>(_env, _programCacheDir));
-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>(std::move(renderer),
+ *this,
+ [this] { this->invalidate(); });
+
+ // 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);
- map->setSourceTileCacheSize(zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f);
+ // initialize egl components
+ _initializeDisplay();
+ _initializeContext();
}
/**
@@ -105,15 +108,15 @@ NativeMapView::~NativeMapView() {
}
/**
- * From mbgl::View
+ * From mbgl::RendererBackend
*/
void NativeMapView::bind() {
setFramebufferBinding(0);
- setViewportSize(getFramebufferSize());
+ setViewport(0, 0, getFramebufferSize());
}
/**
- * From mbgl::Backend.
+ * From mbgl::RendererBackend.
*/
gl::ProcAddress NativeMapView::initializeExtension(const char* name) {
return eglGetProcAddress(name);
@@ -145,7 +148,7 @@ void NativeMapView::activate() {
}
/**
- * From mbgl::Backend.
+ * From mbgl::RendererBackend.
*/
void NativeMapView::deactivate() {
assert(vm != nullptr);
@@ -168,7 +171,7 @@ void NativeMapView::deactivate() {
}
/**
- * From mbgl::Backend. Callback to java NativeMapView#onInvalidate().
+ * From mbgl::RendererBackend. Callback to java NativeMapView#onInvalidate().
*
* May be called from any thread
*/
@@ -179,7 +182,7 @@ void NativeMapView::invalidate() {
}
/**
- * 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,22 +260,6 @@ 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)));
}
@@ -282,14 +269,14 @@ void NativeMapView::destroySurface(jni::JNIEnv&) {
}
void NativeMapView::render(jni::JNIEnv& env) {
- BackendScope guard(*this);
+ BackendScope guard { *this };
if (framebufferSizeChanged) {
- setViewportSize(getFramebufferSize());
+ setViewport(0, 0, getFramebufferSize());
framebufferSizeChanged = false;
}
- map->render(*this);
+ rendererFrontend->render();
if(snapshot){
snapshot = false;
@@ -325,7 +312,6 @@ 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) {
@@ -336,19 +322,19 @@ void NativeMapView::resizeFramebuffer(jni::JNIEnv&, int w, int h) {
}
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 +466,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 +474,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&) {
@@ -599,7 +583,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;
@@ -729,8 +713,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 +727,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 +759,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 +781,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 +801,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 +818,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 +836,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 +863,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 +898,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 +921,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 +933,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 +942,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 +957,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 +965,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 +982,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 +998,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 +1017,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,12 +1034,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));
+ map->getStyle().removeImage(jni::Make<std::string>(env, name));
+}
+
+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>();
+ }
+}
+
+void NativeMapView::setPrefetchesTiles(JNIEnv&, jni::jboolean enable) {
+ map->setPrefetchZoomDelta(enable ? util::DEFAULT_PREFETCH_ZOOM_DELTA : uint8_t(0));
+}
+
+jni::jboolean NativeMapView::getPrefetchesTiles(JNIEnv&) {
+ return jni::jboolean(map->getPrefetchZoomDelta() > 0);
}
// Private methods //
@@ -1421,7 +1434,7 @@ mbgl::Size NativeMapView::getFramebufferSize() const {
void NativeMapView::updateAssumedState() {
assumeFramebufferBinding(0);
- assumeViewportSize(getFramebufferSize());
+ assumeViewport(0, 0, getFramebufferSize());
}
void NativeMapView::updateFps() {
@@ -1463,17 +1476,13 @@ 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::jfloat, jni::String>,
"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"),
@@ -1528,6 +1537,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,7 +1562,10 @@ 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")
);
}
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index c638de44fb..24c88f4e3f 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -1,10 +1,9 @@
#pragma once
-#include <mbgl/map/backend.hpp>
+#include <mbgl/renderer/renderer_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>
@@ -25,6 +24,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 +36,9 @@
namespace mbgl {
namespace android {
-class NativeMapView : public View, public Backend {
+class AndroidRendererFrontend;
+
+class NativeMapView : public RendererBackend, public MapObserver {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; };
@@ -49,25 +51,19 @@ public:
jni::Object<NativeMapView>,
jni::Object<FileSource>,
jni::jfloat pixelRatio,
- jni::String programCacheDir,
- jni::jint availableProcessors,
- jni::jlong totalMemory);
+ jni::String programCacheDir);
virtual ~NativeMapView();
- // mbgl::View //
+ // mbgl::RendererBackend //
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;
@@ -81,9 +77,10 @@ public:
void onDidFinishLoadingStyle() override;
void onSourceChanged(mbgl::style::Source&) override;
- // JNI //
+ // Signal the view system, we want to redraw
+ void invalidate();
- void destroy(jni::JNIEnv&);
+ // JNI //
void render(jni::JNIEnv&);
@@ -93,14 +90,6 @@ public:
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&);
@@ -211,6 +200,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,8 +254,14 @@ public:
void removeImage(JNIEnv&, jni::String);
+ jni::Object<Bitmap> getImage(JNIEnv&, jni::String);
+
+ void setPrefetchesTiles(JNIEnv&, jni::jboolean);
+
+ jni::jboolean getPrefetchesTiles(JNIEnv&);
+
protected:
- // mbgl::Backend //
+ // mbgl::RendererBackend //
gl::ProcAddress initializeExtension(const char*) override;
void activate() override;
@@ -290,7 +287,7 @@ private:
void updateFps();
private:
- void recalculateSourceTileCacheSize();
+ std::unique_ptr<AndroidRendererFrontend> rendererFrontend;
JavaVM *vm = nullptr;
jni::UniqueWeakObject<NativeMapView> javaPeer;
@@ -327,9 +324,6 @@ private:
bool framebufferSizeChanged = true;
- int availableProcessors = 0;
- size_t totalMemory = 0;
-
// Ensure these are initialised last
std::shared_ptr<mbgl::ThreadPool> threadPool;
std::unique_ptr<mbgl::Map> map;
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/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/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..e6321a8efa 100644
--- a/platform/android/src/style/layers/custom_layer.cpp
+++ b/platform/android/src/style/layers/custom_layer.cpp
@@ -21,6 +21,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&) {
diff --git a/platform/android/src/style/layers/custom_layer.hpp b/platform/android/src/style/layers/custom_layer.hpp
index 1173d21bfd..3e3f3bf77f 100644
--- a/platform/android/src/style/layers/custom_layer.hpp
+++ b/platform/android/src/style/layers/custom_layer.hpp
@@ -20,6 +20,8 @@ public:
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..b6cf51ec7e 100644
--- a/platform/android/src/style/layers/symbol_layer.cpp
+++ b/platform/android/src/style/layers/symbol_layer.cpp
@@ -125,6 +125,12 @@ namespace android {
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 +520,7 @@ namespace android {
METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"),
METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"),
METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"),
+ 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..6d3da13ae9 100644
--- a/platform/android/src/style/layers/symbol_layer.hpp
+++ b/platform/android/src/style/layers/symbol_layer.hpp
@@ -59,6 +59,8 @@ public:
jni::Object<jni::ObjectTag> getIconOffset(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/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/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..f02fc98ded 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";
@@ -96,8 +98,8 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
}
std::vector<mbgl::Feature> features;
- if (self.mapView) {
- features = self.mapView.mbglMap->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
+ if (self.style) {
+ features = self.style.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..dde55967d7 100644
--- a/platform/darwin/src/MGLSource.mm
+++ b/platform/darwin/src/MGLSource.mm
@@ -1,7 +1,7 @@
#import "MGLSource_Private.h"
-#import "MGLMapView_Private.h"
+#import "MGLStyle_Private.h"
-#include <mbgl/map/map.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/source.hpp>
@interface MGLSource ()
@@ -10,7 +10,7 @@
// special internal source types like mbgl::AnnotationSource.
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
-@property (nonatomic, readonly, weak) MGLMapView *mapView;
+@property (nonatomic, readonly, weak) MGLStyle *style;
@end
@@ -44,21 +44,21 @@
return self;
}
-- (void)addToMapView:(MGLMapView *)mapView {
+- (void)addToStyle:(MGLStyle *)style {
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, style];
}
- _mapView = mapView;
- mapView.mbglMap->addSource(std::move(_pendingSource));
+ _style = style;
+ 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);
- _mapView = nil;
+- (void)removeFromStyle:(MGLStyle *)style {
+ if (self.rawSource == style.rawStyle->getSource(self.identifier.UTF8String)) {
+ _pendingSource = style.rawStyle->removeSource(self.identifier.UTF8String);
+ _style = nil;
}
}
diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h
index 13a3dd5a30..d83ca3335a 100644
--- a/platform/darwin/src/MGLSource_Private.h
+++ b/platform/darwin/src/MGLSource_Private.h
@@ -18,7 +18,7 @@ struct SourceWrapper {
__weak MGLSource *source;
};
-@class MGLMapView;
+@class MGLStyle;
@interface MGLSource (Private)
@@ -44,33 +44,33 @@ struct SourceWrapper {
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
/**
- The map view whose style currently contains the source.
+ The style which currently contains the source.
- If the source is not currently part of any map view’s style, this property is
+ If the source is not currently part of a style, this property is
set to `nil`.
*/
-@property (nonatomic, readonly, weak) MGLMapView *mapView;
+@property (nonatomic, readonly, weak) MGLStyle *style;
/**
- Adds the mbgl source that this object represents to the mbgl map.
+ Adds the mbgl source that this object represents to the style.
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::Style` and this object no longer has an active unique_ptr reference to the
`mbgl::Source`. If this object's mbgl source is in that state, the mbgl source
can still be changed but the changes will not be visible until the `MGLSource`
- is added back to the map via `-[MGLStyle addSource:]` and styled with a
+ is added back to the style via `-[MGLStyle addSource:]` and styled with a
`MGLLayer`.
*/
-- (void)addToMapView:(MGLMapView *)mapView;
+- (void)addToStyle:(MGLStyle *)style;
/**
- Removes the mbgl source that this object represents from the mbgl map.
+ Removes the mbgl source that this object represents from the style.
When a mbgl source is removed, ownership of the object is transferred back
to the `MGLSource` instance and the unique_ptr reference is valid again. It is
safe to add the source back to the style after it is removed.
*/
-- (void)removeFromMapView:(MGLMapView *)mapView;
+- (void)removeFromStyle:(MGLStyle *)style;
@end
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index 8c009d22c6..98be70be9c 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
@@ -521,40 +484,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/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..d8dded7dbd 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -8,6 +8,27 @@
NS_ASSUME_NONNULL_BEGIN
/**
+ 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.
@@ -477,6 +498,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 +572,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
@@ -881,8 +924,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 +1053,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;
@@ -1921,6 +1984,19 @@ MGL_EXPORT
#pragma mark Working with Symbol Style Layer Attribute Values
/**
+ 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..2541e6b0a4 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -13,6 +13,12 @@
namespace mbgl {
+ MBGL_DEFINE_ENUM(MGLIconPitchAlignment, {
+ { MGLIconPitchAlignmentMap, "map" },
+ { MGLIconPitchAlignmentViewport, "viewport" },
+ { MGLIconPitchAlignmentAuto, "auto" },
+ });
+
MBGL_DEFINE_ENUM(MGLIconRotationAlignment, {
{ MGLIconRotationAlignmentMap, "map" },
{ MGLIconRotationAlignmentViewport, "viewport" },
@@ -259,6 +265,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();
@@ -563,7 +586,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::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toDataDrivenPropertyValue(textAnchor);
self.rawLayer->setTextAnchor(mbglValue);
}
@@ -572,9 +595,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::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextAnchor());
}
- return MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextFontNames:(MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
@@ -652,7 +675,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 +684,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 {
@@ -1321,6 +1344,16 @@ namespace mbgl {
@implementation NSValue (MGLSymbolStyleLayerAdditions)
++ (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..431e0c250c 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 ()
@@ -62,8 +64,8 @@
}
std::vector<mbgl::Feature> features;
- if (self.mapView) {
- features = self.mapView.mbglMap->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
+ if (self.style) {
+ features = self.style.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 d93483ea6e..608cfdfd2d 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -210,9 +210,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]]);
@@ -220,16 +220,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];
@@ -237,7 +237,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..6b0b20354b 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -301,6 +301,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(),
@@ -892,7 +931,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::TextAnchorType> propertyValue = { mbgl::style::TextAnchorType::BottomRight };
XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue,
@"Setting textAnchor to a constant value should update text-anchor.");
XCTAssertEqualObjects(layer.textAnchor, constantStyleValue,
@@ -916,11 +955,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 +1100,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 +1124,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
@@ -2321,6 +2350,7 @@
[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 +2396,9 @@
}
- (void)testValueAdditions {
+ 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..9c8a38a308 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,10 @@ namespace mbgl {
class DefaultFileSource::Impl {
public:
- Impl(const std::string& cachePath, uint64_t maximumCacheSize)
- : offlineDatabase(cachePath, maximumCacheSize) {
+ Impl(ActorRef<Impl>, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize)
+ : assetFileSource(assetFileSource_)
+ , localFileSource(std::make_unique<LocalFileSource>())
+ , offlineDatabase(cachePath, maximumCacheSize) {
}
void setAPIBaseURL(const std::string& url) {
@@ -46,7 +50,7 @@ public:
return onlineFileSource.getAccessToken();
}
- void setResourceTransform(OnlineFileSource::ResourceTransform&& transform) {
+ void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
onlineFileSource.setResourceTransform(std::move(transform));
}
@@ -104,36 +108,60 @@ 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
+ Resource revalidation = resource;
+
+ const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires;
+ if (!hasPrior || resource.necessity == Resource::Optional) {
+ auto offlineResponse = offlineDatabase.get(resource);
+
+ if (resource.necessity == Resource::Optional && !offlineResponse) {
+ // Ensure there's always a response that we can send, so the caller knows that
+ // there's no optional data available in the cache.
+ offlineResponse.emplace();
+ offlineResponse->noContent = true;
+ offlineResponse->error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, "Not found in offline database");
+ }
+
+ if (offlineResponse) {
+ revalidation.priorModified = offlineResponse->modified;
+ revalidation.priorExpires = offlineResponse->expires;
+ revalidation.priorEtag = offlineResponse->etag;
+
+ // 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 {
+ // 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.
+ revalidation.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(revalidation, [=] (Response onlineResponse) mutable {
+ this->offlineDatabase.put(revalidation, 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) {
@@ -158,6 +186,9 @@ private:
std::make_unique<OfflineDownload>(regionID, offlineDatabase.getRegionDefinition(regionID), offlineDatabase, onlineFileSource)).first->second;
}
+ // shared so that destruction is done on the creating thread
+ const std::shared_ptr<FileSource> assetFileSource;
+ const std::unique_ptr<FileSource> localFileSource;
OfflineDatabase offlineDatabase;
OnlineFileSource onlineFileSource;
std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
@@ -173,118 +204,102 @@ 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::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..fe77b80985 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,29 @@ 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 };
+}
+
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..8d86ea8c47 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,19 @@ 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;
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 +41,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..ad03706be7
--- /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)
+ : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler) {
+}
+
+HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler)
+ : 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)) {
+}
+
+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..18d0d2527b
--- /dev/null
+++ b/platform/default/mbgl/gl/headless_frontend.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/gl/headless_backend.hpp>
+#include <mbgl/util/async_task.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&);
+ HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&);
+ ~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/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp
index 02736f10a4..65c2097182 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -49,8 +49,9 @@ void OfflineDatabase::ensureSchema() {
case 2: migrateToVersion3(); // fall through
case 3: // no-op and fall through
case 4: migrateToVersion5(); // fall through
- case 5: return;
- default: throw std::runtime_error("unknown schema version");
+ case 5: migrateToVersion6(); // fall through
+ case 6: return;
+ default: break; // downgrade, delete the database
}
removeExisting();
@@ -83,7 +84,7 @@ void OfflineDatabase::ensureSchema() {
db->exec("PRAGMA journal_mode = DELETE");
db->exec("PRAGMA synchronous = FULL");
db->exec(schema);
- db->exec("PRAGMA user_version = 5");
+ db->exec("PRAGMA user_version = 6");
} catch (...) {
Log::Error(Event::Database, "Unexpected error creating database schema: %s", util::toString(std::current_exception()).c_str());
throw;
@@ -126,6 +127,14 @@ void OfflineDatabase::migrateToVersion5() {
db->exec("PRAGMA user_version = 5");
}
+void OfflineDatabase::migrateToVersion6() {
+ mapbox::sqlite::Transaction transaction(*db);
+ db->exec("ALTER TABLE resources ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0");
+ db->exec("ALTER TABLE tiles ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0");
+ db->exec("PRAGMA user_version = 6");
+ transaction.commit();
+}
+
OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) {
auto it = statements.find(sql);
@@ -188,11 +197,11 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource,
if (resource.kind == Resource::Kind::Tile) {
assert(resource.tileData);
inserted = putTile(*resource.tileData, response,
- compressed ? compressedData : *response.data,
+ compressed ? compressedData : response.data ? *response.data : "",
compressed);
} else {
inserted = putResource(resource, response,
- compressed ? compressedData : *response.data,
+ compressed ? compressedData : response.data ? *response.data : "",
compressed);
}
@@ -211,8 +220,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
// clang-format off
Statement stmt = getStatement(
- // 0 1 2 3 4
- "SELECT etag, expires, modified, data, compressed "
+ // 0 1 2 3 4 5
+ "SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM resources "
"WHERE url = ?");
// clang-format on
@@ -226,14 +235,15 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.modified = stmt->get<optional<Timestamp>>(2);
+ response.etag = stmt->get<optional<std::string>>(0);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.mustRevalidate = stmt->get<bool>(2);
+ response.modified = stmt->get<optional<Timestamp>>(3);
- optional<std::string> data = stmt->get<optional<std::string>>(3);
+ optional<std::string> data = stmt->get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<int>(4)) {
+ } else if (stmt->get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -265,14 +275,16 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement update = getStatement(
"UPDATE resources "
- "SET accessed = ?1, "
- " expires = ?2 "
- "WHERE url = ?3 ");
+ "SET accessed = ?1, "
+ " expires = ?2, "
+ " must_revalidate = ?3 "
+ "WHERE url = ?4 ");
// clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
- update->bind(3, resource.url);
+ update->bind(3, response.mustRevalidate);
+ update->bind(4, resource.url);
update->run();
return false;
}
@@ -286,29 +298,31 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement update = getStatement(
"UPDATE resources "
- "SET kind = ?1, "
- " etag = ?2, "
- " expires = ?3, "
- " modified = ?4, "
- " accessed = ?5, "
- " data = ?6, "
- " compressed = ?7 "
- "WHERE url = ?8 ");
+ "SET kind = ?1, "
+ " etag = ?2, "
+ " expires = ?3, "
+ " must_revalidate = ?4, "
+ " modified = ?5, "
+ " accessed = ?6, "
+ " data = ?7, "
+ " compressed = ?8 "
+ "WHERE url = ?9 ");
// clang-format on
update->bind(1, int(resource.kind));
update->bind(2, response.etag);
update->bind(3, response.expires);
- update->bind(4, response.modified);
- update->bind(5, util::now());
- update->bind(8, resource.url);
+ update->bind(4, response.mustRevalidate);
+ update->bind(5, response.modified);
+ update->bind(6, util::now());
+ update->bind(9, resource.url);
if (response.noContent) {
- update->bind(6, nullptr);
- update->bind(7, false);
+ update->bind(7, nullptr);
+ update->bind(8, false);
} else {
- update->bindBlob(6, data.data(), data.size(), false);
- update->bind(7, compressed);
+ update->bindBlob(7, data.data(), data.size(), false);
+ update->bind(8, compressed);
}
update->run();
@@ -319,23 +333,24 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement insert = getStatement(
- "INSERT INTO resources (url, kind, etag, expires, modified, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) ");
+ "INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ");
// clang-format on
insert->bind(1, resource.url);
insert->bind(2, int(resource.kind));
insert->bind(3, response.etag);
insert->bind(4, response.expires);
- insert->bind(5, response.modified);
- insert->bind(6, util::now());
+ insert->bind(5, response.mustRevalidate);
+ insert->bind(6, response.modified);
+ insert->bind(7, util::now());
if (response.noContent) {
- insert->bind(7, nullptr);
- insert->bind(8, false);
+ insert->bind(8, nullptr);
+ insert->bind(9, false);
} else {
- insert->bindBlob(7, data.data(), data.size(), false);
- insert->bind(8, compressed);
+ insert->bindBlob(8, data.data(), data.size(), false);
+ insert->bind(9, compressed);
}
insert->run();
@@ -366,8 +381,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
// clang-format off
Statement stmt = getStatement(
- // 0 1 2 3 4
- "SELECT etag, expires, modified, data, compressed "
+ // 0 1 2, 3, 4, 5
+ "SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM tiles "
"WHERE url_template = ?1 "
" AND pixel_ratio = ?2 "
@@ -389,14 +404,15 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.modified = stmt->get<optional<Timestamp>>(2);
+ response.etag = stmt->get<optional<std::string>>(0);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.mustRevalidate = stmt->get<bool>(2);
+ response.modified = stmt->get<optional<Timestamp>>(3);
- optional<std::string> data = stmt->get<optional<std::string>>(3);
+ optional<std::string> data = stmt->get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<int>(4)) {
+ } else if (stmt->get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -440,22 +456,24 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement update = getStatement(
"UPDATE tiles "
- "SET accessed = ?1, "
- " expires = ?2 "
- "WHERE url_template = ?3 "
- " AND pixel_ratio = ?4 "
- " AND x = ?5 "
- " AND y = ?6 "
- " AND z = ?7 ");
+ "SET accessed = ?1, "
+ " expires = ?2, "
+ " must_revalidate = ?3 "
+ "WHERE url_template = ?4 "
+ " AND pixel_ratio = ?5 "
+ " AND x = ?6 "
+ " AND y = ?7 "
+ " AND z = ?8 ");
// clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
- update->bind(3, tile.urlTemplate);
- update->bind(4, tile.pixelRatio);
- update->bind(5, tile.x);
- update->bind(6, tile.y);
- update->bind(7, tile.z);
+ update->bind(3, response.mustRevalidate);
+ update->bind(4, tile.urlTemplate);
+ update->bind(5, tile.pixelRatio);
+ update->bind(6, tile.x);
+ update->bind(7, tile.y);
+ update->bind(8, tile.z);
update->run();
return false;
}
@@ -469,35 +487,37 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement update = getStatement(
"UPDATE tiles "
- "SET modified = ?1, "
- " etag = ?2, "
- " expires = ?3, "
- " accessed = ?4, "
- " data = ?5, "
- " compressed = ?6 "
- "WHERE url_template = ?7 "
- " AND pixel_ratio = ?8 "
- " AND x = ?9 "
- " AND y = ?10 "
- " AND z = ?11 ");
+ "SET modified = ?1, "
+ " etag = ?2, "
+ " expires = ?3, "
+ " must_revalidate = ?4, "
+ " accessed = ?5, "
+ " data = ?6, "
+ " compressed = ?7 "
+ "WHERE url_template = ?8 "
+ " AND pixel_ratio = ?9 "
+ " AND x = ?10 "
+ " AND y = ?11 "
+ " AND z = ?12 ");
// clang-format on
update->bind(1, response.modified);
update->bind(2, response.etag);
update->bind(3, response.expires);
- update->bind(4, util::now());
- update->bind(7, tile.urlTemplate);
- update->bind(8, tile.pixelRatio);
- update->bind(9, tile.x);
- update->bind(10, tile.y);
- update->bind(11, tile.z);
+ update->bind(4, response.mustRevalidate);
+ update->bind(5, util::now());
+ update->bind(8, tile.urlTemplate);
+ update->bind(9, tile.pixelRatio);
+ update->bind(10, tile.x);
+ update->bind(11, tile.y);
+ update->bind(12, tile.z);
if (response.noContent) {
- update->bind(5, nullptr);
- update->bind(6, false);
+ update->bind(6, nullptr);
+ update->bind(7, false);
} else {
- update->bindBlob(5, data.data(), data.size(), false);
- update->bind(6, compressed);
+ update->bindBlob(6, data.data(), data.size(), false);
+ update->bind(7, compressed);
}
update->run();
@@ -508,8 +528,8 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement insert = getStatement(
- "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, etag, expires, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11) ");
+ "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, must_revalidate, etag, expires, accessed, data, compressed) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)");
// clang-format on
insert->bind(1, tile.urlTemplate);
@@ -518,16 +538,17 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
insert->bind(4, tile.y);
insert->bind(5, tile.z);
insert->bind(6, response.modified);
- insert->bind(7, response.etag);
- insert->bind(8, response.expires);
- insert->bind(9, util::now());
+ insert->bind(7, response.mustRevalidate);
+ insert->bind(8, response.etag);
+ insert->bind(9, response.expires);
+ insert->bind(10, util::now());
if (response.noContent) {
- insert->bind(10, nullptr);
- insert->bind(11, false);
+ insert->bind(11, nullptr);
+ insert->bind(12, false);
} else {
- insert->bindBlob(10, data.data(), data.size(), false);
- insert->bind(11, compressed);
+ insert->bindBlob(11, data.data(), data.size(), false);
+ insert->bind(12, compressed);
}
insert->run();
diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp
index 57ffcee4eb..91b544a9e0 100644
--- a/platform/default/mbgl/storage/offline_database.hpp
+++ b/platform/default/mbgl/storage/offline_database.hpp
@@ -64,6 +64,7 @@ private:
void removeExisting();
void migrateToVersion3();
void migrateToVersion5();
+ void migrateToVersion6();
class Statement {
public:
diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp
index 901f996a4f..7f0001f64b 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,22 +75,15 @@ 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();
} 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;
@@ -99,12 +96,32 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
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..1c594f87a0 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();
}
@@ -145,7 +149,7 @@ public:
return activeRequests.find(request) != activeRequests.end();
}
- void setResourceTransform(ResourceTransform&& transform) {
+ void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
resourceTransform = std::move(transform);
}
@@ -156,7 +160,7 @@ private:
}
}
- ResourceTransform resourceTransform;
+ optional<ActorRef<ResourceTransform>> resourceTransform;
/**
* The lifetime of a request is:
@@ -189,6 +193,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 +221,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 +320,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 +374,19 @@ 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);
+}
+
} // 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..784e908878 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,8 @@ 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 `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 +141,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);
@@ -163,17 +180,6 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
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 +202,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 +237,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 +303,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 +328,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 +345,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 +393,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 +415,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 +437,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 +465,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 +495,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 +513,6 @@ void GLFWView::run() {
invalidate();
}
- dirty = false;
}
};
@@ -540,6 +581,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..ccde4f027f 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.
@@ -31,16 +33,19 @@ public:
void setWindowTitle(const std::string&);
void run();
+
+ void invalidate();
- // mbgl::View implementation
- void bind() override;
mbgl::Size getSize() const;
mbgl::Size getFramebufferSize() const;
- // 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 +66,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 +82,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 +111,7 @@ private:
std::function<void()> changeStyleCallback;
std::function<void()> pauseResumeCallback;
+ 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..3702b8aaab 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) {
@@ -39,15 +42,15 @@ int main(int argc, char *argv[]) {
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'},
+ {"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,9 +59,6 @@ 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;
@@ -99,7 +99,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.");
@@ -119,8 +119,8 @@ 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);
@@ -147,7 +147,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 +178,7 @@ int main(int argc, char *argv[]) {
}
}
- map.setStyleURL(style);
+ map.getStyle().loadURL(style);
view->run();
@@ -194,9 +194,9 @@ int main(int argc, char *argv[]) {
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/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index e657472aa5..5c74dcf223 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -2,6 +2,18 @@
Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
+## master
+
+### Styles
+
+* Added support for displaying geo-referenced images via the `MGLImageSource`. [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
+* The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
+
+### Other changes
+
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
## 3.6.2
* Added an `MGLStyle.localizesLabels` property, off by default, that localizes any Mapbox Streets–sourced symbol layer into the user’s preferred language. ([#9582](https://github.com/mapbox/mapbox-gl-native/pull/9582))
diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake
index 836d0678cb..7ea1dddef7 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,13 @@ 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
# 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/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 a514d15b41..023ed1b437 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
@@ -585,7 +597,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) double maximumZoomLevel;
@@ -1137,6 +1150,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 715c32186d..d9d03ea478 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"
@@ -277,6 +279,8 @@ public:
{
mbgl::Map *_mbglMap;
MBGLView *_mbglView;
+ std::unique_ptr<MGLRenderFrontend> _rendererFrontend;
+
std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
BOOL _opaque;
@@ -379,7 +383,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;
}
@@ -395,12 +399,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;
}
@@ -409,6 +413,11 @@ public:
return _mbglMap;
}
+- (mbgl::Renderer *)renderer
+{
+ return _rendererFrontend->getRenderer();
+}
+
- (void)commonInit
{
_isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
@@ -443,8 +452,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) {
@@ -758,47 +769,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;
@@ -846,12 +821,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];
}
@@ -2179,10 +2151,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];
@@ -2190,7 +2163,7 @@ public:
- (void)emptyMemoryCache
{
- _mbglMap->onLowMemory();
+ _rendererFrontend->onLowMemory();
}
- (void)setZoomEnabled:(BOOL)zoomEnabled
@@ -2573,8 +2546,6 @@ public:
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
{
- _mbglMap->setMinZoom(minimumZoomLevel);
- [self validateTileCacheSize];
}
- (double)minimumZoomLevel
@@ -2585,7 +2556,6 @@ public:
- (void)setMaximumZoomLevel:(double)maximumZoomLevel
{
_mbglMap->setMaxZoom(maximumZoomLevel);
- [self validateTileCacheSize];
}
- (double)maximumZoomLevel
@@ -3463,7 +3433,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
@@ -3546,6 +3516,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 ]];
@@ -3749,7 +3735,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) },
});
@@ -4749,7 +4735,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);
}
@@ -4782,7 +4768,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);
}
@@ -5008,7 +4994,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];
@@ -5450,7 +5436,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_) {
@@ -5461,7 +5447,7 @@ public:
/// context state with the anticipated values.
void updateAssumedState() override {
assumeFramebufferBinding(ImplicitFramebufferBinding);
- assumeViewportSize(nativeView.framebufferSize);
+ assumeViewport(0, 0, nativeView.framebufferSize);
}
void bind() override {
@@ -5474,7 +5460,7 @@ public:
updateAssumedState();
} else {
// Our framebuffer is still bound, but the viewport might have changed.
- setViewportSize(nativeView.framebufferSize);
+ setViewport(0, 0, nativeView.framebufferSize);
}
}
@@ -5562,11 +5548,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..abe16cc3ee 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"
diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h
index 642355d412..6e15e07cb5 100644
--- a/platform/ios/src/UIImage+MGLAdditions.h
+++ b/platform/ios/src/UIImage+MGLAdditions.h
@@ -8,7 +8,9 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage;
-- (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/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm
index db64d78232..5e28d18190 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,14 @@
return self;
}
-- (std::unique_ptr<mbgl::style::Image>)mgl_styleImage {
+- (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..3aa1fdbbfc 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)
@@ -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_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 b8ed40cde6..8707c0aad7 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog for Mapbox macOS SDK
+## master
+
+### Styles
+
+* Added support for displaying geo-referenced images via the `MGLImageSource`. [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
+* The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
+
+### Other changes
+
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
## 0.5.1
This version of the Mapbox macOS SDK corresponds to version 3.6.2 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..9a8cf05c16 100644
--- a/platform/macos/app/Base.lproj/MainMenu.xib
+++ b/platform/macos/app/Base.lproj/MainMenu.xib
@@ -554,6 +554,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>
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 1d22295f50..d6855d3ff2 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -572,6 +572,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 +681,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 +941,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"];
}
diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake
index 1b8ea35814..86c54b612c 100644
--- a/platform/macos/config.cmake
+++ b/platform/macos/config.cmake
@@ -1,6 +1,6 @@
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)
@@ -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,13 @@ 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
# Thread pool
PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
@@ -90,6 +91,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 +101,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 +111,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 00cb6ce975..34b08c0035 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, ); }; };
@@ -79,6 +82,7 @@
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE7A51E56161C00C7916D /* MGLFoundation.mm */; };
55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.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 +276,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>"; };
@@ -348,6 +355,7 @@
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>"; };
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>"; };
@@ -666,6 +674,8 @@
DA8F25951D51CAC70010E6B5 /* MGLVectorSource.h */,
DA7DC9801DED5F5C0027472F /* MGLVectorSource_Private.h */,
DA8F25961D51CAC70010E6B5 /* MGLVectorSource.mm */,
+ 07A019EB1ED662D800ACD43E /* MGLImageSource.h */,
+ 07A019EC1ED662D800ACD43E /* MGLImageSource.mm */,
);
name = Sources;
sourceTree = "<group>";
@@ -774,6 +784,7 @@
DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */,
DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */,
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */,
+ 07BA4CAB1EE21887004528F5 /* MGLImageSourceTests.m */,
);
name = Sources;
sourceTree = "<group>";
@@ -1039,6 +1050,7 @@
DAE6C36E1CC31E2A00DB3429 /* MGLMapCamera.mm */,
DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */,
DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */,
+ 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */,
DAE6C3571CC31E0400DB3429 /* MGLStyle.h */,
3537CA731D3F93A600380318 /* MGLStyle_Private.h */,
DAE6C37A1CC31E2A00DB3429 /* MGLStyle.mm */,
@@ -1144,6 +1156,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 */,
@@ -1169,6 +1182,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 */,
@@ -1388,6 +1402,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 07A019EF1ED665CD00ACD43E /* MGLImageSource.mm in Sources */,
40ABDB561DB0022100372083 /* NSImage+MGLAdditions.mm in Sources */,
DAE6C3901CC31E2A00DB3429 /* MGLPointAnnotation.mm in Sources */,
DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */,
@@ -1481,6 +1496,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 fb715a506d..2fa32ae1e4 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) 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..5dbb134ef5 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,12 @@ 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::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..e4ad258b6e 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -50,6 +50,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 "NSValue+MGLAdditions.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_map.cpp b/platform/node/src/node_map.cpp
index 1c8bf6d1d2..7c7082bd09 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,7 +52,6 @@ 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, "addLayer", AddLayer);
Nan::SetPrototypeMethod(tpl, "removeLayer", RemoveLayer);
@@ -79,9 +70,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();
}
/**
@@ -214,7 +202,7 @@ 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());
}
@@ -266,11 +254,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 +266,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,19 +354,8 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
void NodeMap::startRender(NodeMap::RenderOptions options) {
- map->setSize({ options.width, options.height });
-
- 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);
- }
+ frontend->setSize(options.size);
+ map->setSize(options.size);
if (map->getZoom() != options.zoom) {
map->setZoom(options.zoom);
@@ -401,13 +378,13 @@ void NodeMap::startRender(NodeMap::RenderOptions options) {
map->setDebug(options.debugOptions);
}
- map->renderStill(*view, [this](const std::exception_ptr eptr) {
+ map->renderStill([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 +483,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 +509,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;
@@ -585,7 +549,7 @@ 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::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -606,7 +570,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 +588,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 +624,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 +635,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 +650,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 +668,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 +686,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 +718,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 +727,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 +771,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 +870,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 +908,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 +933,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 +944,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 +968,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..7bd3b99bea 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,7 +44,6 @@ 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 AddLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
@@ -69,9 +70,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..4d4e0edbe4
--- /dev/null
+++ b/platform/node/test/ignores.json
@@ -0,0 +1,70 @@
+{
+ "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/circle-pitch-alignment/map-scale-map": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
+ "render-tests/circle-pitch-alignment/map-scale-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
+ "render-tests/circle-pitch-alignment/viewport-scale-map": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
+ "render-tests/circle-pitch-alignment/viewport-scale-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
+ "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/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
+ "render-tests/regressions/mapbox-gl-js#2929": "skip - needs issue",
+ "render-tests/regressions/mapbox-gl-js#3010": "skip - needs issue",
+ "render-tests/regressions/mapbox-gl-js#3548": "skip - needs issue",
+ "render-tests/regressions/mapbox-gl-js#3682": "https://github.com/mapbox/mapbox-gl-js/issues/3682",
+ "render-tests/regressions/mapbox-gl-js#4550": "skip - https://github.com/mapbox/mapbox-gl-native/issues/1350",
+ "render-tests/regressions/mapbox-gl-js#4551": "skip - https://github.com/mapbox/mapbox-gl-native/issues/1350",
+ "render-tests/regressions/mapbox-gl-js#4573": "skip - https://github.com/mapbox/mapbox-gl-native/issues/1350",
+ "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357",
+ "render-tests/regressions/mapbox-gl-native#9792": "skip - https://github.com/mapbox/mapbox-gl-native/issues/9792",
+ "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "skip - https://github.com/mapbox/mapbox-gl-native/issues/6745",
+ "render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "skip - needs issue",
+ "render-tests/runtime-styling/source-add-geojson-inline": "skip - needs issue",
+ "render-tests/symbol-placement/line": "needs issue",
+ "render-tests/text-keep-upright/line-placement-true-offset": "https://github.com/mapbox/mapbox-gl-native/issues/9271",
+ "render-tests/text-letter-spacing/property-function": "https://github.com/mapbox/mapbox-gl-native/issues/9668",
+ "render-tests/text-letter-spacing/zoom-and-property-function": "https://github.com/mapbox/mapbox-gl-native/issues/9668",
+ "render-tests/text-max-width/property-function": "https://github.com/mapbox/mapbox-gl-native/issues/9654",
+ "render-tests/text-max-width/zoom-and-property-function": "https://github.com/mapbox/mapbox-gl-native/issues/9654",
+ "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-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-function-high-base": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
+ "render-tests/video/default": "skip - needs issue"
+}
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index 4ab76b937a..04d02d0558 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -108,7 +108,6 @@ test('Map', function(t) {
'render',
'release',
'cancel',
- 'addClass',
'addSource',
'addLayer',
'removeLayer',
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..c8a214e919 100644
--- a/platform/node/test/render.test.js
+++ b/platform/node/test/render.test.js
@@ -1,12 +1,13 @@
'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;
+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/suite_implementation.js b/platform/node/test/suite_implementation.js
index 8ac372b7c3..b717ecd2b2 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -70,7 +70,7 @@ 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])));
map.addImage(operation[1], img.data, {
@@ -80,6 +80,11 @@ module.exports = function (style, options, callback) {
});
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..c7c4cf1e4a 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()));
@@ -1590,12 +1534,12 @@ mbgl::Size QMapboxGLPrivate::framebufferSize() const {
void QMapboxGLPrivate::updateAssumedState() {
assumeFramebufferBinding(fbObject);
- assumeViewportSize(framebufferSize());
+ assumeViewport(0, 0, framebufferSize());
}
void QMapboxGLPrivate::bind() {
setFramebufferBinding(fbObject);
- setViewportSize(framebufferSize());
+ setViewport(0, 0, framebufferSize());
}
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..7b0dd8c192 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
@@ -22,12 +22,9 @@ public:
mbgl::Size framebufferSize() const;
- // mbgl::View implementation.
+ // mbgl::RendererBackend implementation.
void bind() final;
-
- // mbgl::Backend implementation.
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..b1d0ed28ec 100644
--- a/scripts/generate-style-code.js
+++ b/scripts/generate-style-code.js
@@ -53,7 +53,7 @@ global.evaluatedType = function (property) {
}
};
-function attributeType(property, type) {
+function attributeUniformType(property, type) {
const attributeNameExceptions = {
'text-opacity': 'opacity',
'icon-opacity': 'opacity',
@@ -64,11 +64,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 +82,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/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..dbf8387ae0 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,16 +19,22 @@ 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);
@@ -37,21 +43,15 @@ AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, cons
}
Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
+ std::lock_guard<std::mutex> lock(mutex);
return Annotation::visit(annotation, [&] (const auto& annotation_) {
return this->update(id, annotation_, maxZoom);
});
}
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);
}
void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) {
@@ -63,19 +63,13 @@ 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) {
@@ -89,16 +83,11 @@ Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation&
const SymbolAnnotation& existing = it->second->annotation;
- if (existing.geometry != annotation.geometry) {
+ if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) {
result |= Update::AnnotationData;
- }
-
- if (existing.icon != annotation.icon) {
- result |= Update::AnnotationData | Update::AnnotationStyle;
- }
- if (result != Update::Nothing) {
- removeAndAdd(id, annotation, maxZoom);
+ remove(id);
+ add(id, annotation, maxZoom);
}
return result;
@@ -110,8 +99,10 @@ Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& a
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);
+ return Update::AnnotationData;
}
Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
@@ -120,25 +111,23 @@ Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& a
assert(false); // Attempt to update a non-existent shape annotation
return Update::Nothing;
}
- removeAndAdd(id, annotation, maxZoom);
- return Update::AnnotationData | Update::AnnotationStyle;
-}
-Update AnnotationManager::update(const AnnotationID& id, const StyleSourcedAnnotation& 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);
+ return Update::AnnotationData;
}
-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 +136,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 +152,84 @@ 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() {
+ std::lock_guard<std::mutex> lock(mutex);
for (auto& tile : tiles) {
tile->setData(getTileData(tile->id.canonical));
}
}
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..dee823bc0f 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -2,13 +2,15 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/annotation/symbol_annotation_impl.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/style/image.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mutex>
#include <string>
#include <vector>
#include <unordered_set>
+#include <unordered_map>
namespace mbgl {
@@ -20,24 +22,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);
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 +52,21 @@ 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 removeAndAdd(const AnnotationID&, const Annotation&, const uint8_t);
+ void remove(const AnnotationID&);
+
+ void updateStyle();
std::unique_ptr<AnnotationTileData> getTileData(const CanonicalTileID&);
+ std::reference_wrapper<style::Style> style;
+
+ std::mutex mutex;
+
AnnotationID nextID = 0;
using SymbolAnnotationTree = boost::geometry::index::rtree<std::shared_ptr<const SymbolAnnotationImpl>, boost::geometry::index::rstar<16, 4>>;
@@ -68,13 +74,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..c2e6191d1d 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 RenderStyle& style,
+ const RenderedQueryOptions& options) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, style, 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..4000c4b04a 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 RenderStyle& style,
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 c9a99ffb8d..e41ff85f33 100644
--- a/src/mbgl/annotation/symbol_annotation_impl.hpp
+++ b/src/mbgl/annotation/symbol_annotation_impl.hpp
@@ -17,7 +17,10 @@
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#ifndef __clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#pragma GCC diagnostic ignored "-Wmisleading-indentation"
+#endif
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
@@ -26,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;
@@ -47,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 <>
@@ -61,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..4c4e985369 100644
--- a/src/mbgl/geometry/feature_index.cpp
+++ b/src/mbgl/geometry/feature_index.cpp
@@ -1,12 +1,12 @@
#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/style/style.hpp>
+#include <mbgl/renderer/render_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>
@@ -54,14 +54,14 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature&
}
static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions,
- const style::Style& style,
+ const RenderStyle& style,
const GeometryTile& tile,
const float pixelsToTileUnits) {
// Determine the additional radius needed factoring in property functions
float additionalRadius = 0;
auto getQueryRadius = [&](const RenderLayer& layer) {
- auto bucket = tile.getBucket(layer);
+ auto bucket = tile.getBucket(*layer.baseImpl);
if (bucket) {
additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits);
}
@@ -69,7 +69,7 @@ static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions
if (queryOptions.layerIDs) {
for (const auto& layerID : *queryOptions.layerIDs) {
- RenderLayer* layer = style.getRenderLayer(layerID);
+ const RenderLayer* layer = style.getRenderLayer(layerID);
if (layer) {
getQueryRadius(*layer);
}
@@ -92,7 +92,7 @@ void FeatureIndex::query(
const RenderedQueryOptions& queryOptions,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const style::Style& style,
+ const RenderStyle& style,
const CollisionTile* collisionTile,
const GeometryTile& tile) const {
@@ -135,7 +135,7 @@ void FeatureIndex::addFeature(
const RenderedQueryOptions& options,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const style::Style& style,
+ const RenderStyle& style,
const float bearing,
const float pixelsToTileUnits) const {
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index f7aa0182e4..83f339a9de 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -13,10 +13,7 @@ namespace mbgl {
class GeometryTile;
class RenderedQueryOptions;
-
-namespace style {
-class Style;
-} // namespace style
+class RenderStyle;
class CollisionTile;
class CanonicalTileID;
@@ -45,7 +42,7 @@ public:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const style::Style&,
+ const RenderStyle&,
const CollisionTile*,
const GeometryTile& tile) const;
@@ -66,7 +63,7 @@ private:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const style::Style&,
+ const RenderStyle&,
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..e18f1e0bcf 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,6 +60,28 @@ 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;
@@ -45,7 +91,7 @@ Context::~Context() {
}
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 +140,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 +210,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 +329,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()));
@@ -419,6 +468,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.
@@ -488,8 +538,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();
@@ -509,12 +559,12 @@ void Context::setDirtyState() {
program.setDirty();
lineWidth.setDirty();
activeTexture.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
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 8929d24e54..9923567276 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> {
@@ -198,6 +201,7 @@ public:
State<value::ActiveTexture> activeTexture;
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 +209,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 +242,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/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..89014fe6bc 100644
--- a/src/mbgl/gl/value.cpp
+++ b/src/mbgl/gl/value.cpp
@@ -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..19e9af194f 100644
--- a/src/mbgl/gl/value.hpp
+++ b/src/mbgl/gl/value.hpp
@@ -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..229b8f2ee2 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,66 @@ 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.get<TextMaxWidth>() * 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.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 */ 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<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 +247,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 +260,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 +291,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 +318,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 +418,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 +464,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 +487,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 +511,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 +526,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 +541,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 +559,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 +600,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..8ccd6a0410
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -0,0 +1,324 @@
+#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;
+ }
+
+
+ Point<float> 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]) };
+ }
+
+ 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
+ };
+
+ optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip,
+ Point<float> anchorPoint, 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 = anchorPoint;
+ Point<float> prev = anchorPoint;
+ 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;
+ current = project(convertPoint<float>(line.at(currentIndex)), 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 float fontScale = fontSize / 24.0;
+ const float lineOffsetX = symbol.lineOffset[0] * fontSize;
+ const float lineOffsetY = symbol.lineOffset[1] * fontSize;
+
+ const Point<float> anchorPoint = project(symbol.anchorPoint, labelPlaneMatrix);
+
+ 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, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ if (!firstPlacedGlyph)
+ return PlacementResult::NotEnoughRoom;
+
+ optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ if (!lastPlacedGlyph)
+ return PlacementResult::NotEnoughRoom;
+
+ const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix);
+ const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix);
+
+ 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, 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(convertPoint<float>(symbol.line.at(symbol.segment)), posMatrix);
+ const Point<float> b = project(convertPoint<float>(symbol.line.at(symbol.segment + 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, 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;
+
+ PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray);
+
+ if (placeUnflipped == PlacementResult::NotEnoughRoom ||
+ (placeUnflipped == PlacementResult::NeedsFlipping &&
+ placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray) == 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..d5a5923e6c 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 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,226 +159,74 @@ 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);
}
-void Map::Impl::renderStill() {
- if (!stillImageRequest) {
- return;
- }
-
- // TODO: determine whether we need activate/deactivate
- BackendScope guard(backend);
- render(stillImageRequest->view);
-}
-
void Map::triggerRepaint() {
- impl->backend.invalidate();
-}
-
-void Map::render(View& view) {
- impl->render(view);
+ impl->onUpdate(Update::Repaint);
}
-void Map::Impl::render(View& view) {
- if (!style) {
- return;
- }
-
- TimePoint timePoint = mode == MapMode::Continuous
- ? Clock::now()
- : Clock::time_point::max();
+#pragma mark - Map::Impl RendererObserver
- 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
- });
-
- 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()) {
+ if (needsRepaint || transform.inTransition()) {
onUpdate(Update::Repaint);
}
- } else if (stillImageRequest && style->isLoaded()) {
- FrameData frameData { timePoint,
- pixelRatio,
- mode,
- contextMode,
- debugOptions };
-
- backend.updateAssumedState();
-
- painter->render(*style,
- frameData,
- view,
- annotationManager->getSpriteAtlas());
-
- 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;
+ } else if (stillImageRequest) {
+ auto request = std::move(stillImageRequest);
+ request->callback(nullptr);
}
+};
- 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
@@ -787,232 +604,31 @@ 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(Update::AnnotationData);
return result;
}
void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) {
- impl->onUpdate(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom()));
+ 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>()));
- }
- 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 {};
+ impl->annotationManager.removeAnnotation(annotation);
+ impl->onUpdate(Update::AnnotationData);
}
#pragma mark - Toggles
@@ -1050,83 +666,71 @@ MapDebugOptions Map::getDebug() const {
return impl->debugOptions;
}
-bool Map::isFullyLoaded() const {
- return impl->style ? impl->style->isLoaded() : false;
+void Map::setPrefetchZoomDelta(uint8_t delta) {
+ impl->prefetchZoomDelta = delta;
}
-void Map::addClass(const std::string& className) {
- if (impl->style && impl->style->addClass(className)) {
- impl->onUpdate(Update::Classes);
- }
+uint8_t Map::getPrefetchZoomDelta() const {
+ return impl->prefetchZoomDelta;
}
-void Map::removeClass(const std::string& className) {
- if (impl->style && impl->style->removeClass(className)) {
- impl->onUpdate(Update::Classes);
- }
-}
-
-void Map::setClasses(const std::vector<std::string>& classNames) {
- if (impl->style) {
- impl->style->setClasses(classNames);
- impl->onUpdate(Update::Classes);
- }
+bool Map::isFullyLoaded() const {
+ return impl->style->impl->isLoaded() && impl->rendererFullyLoaded;
}
-style::TransitionOptions Map::getTransitionOptions() const {
- if (impl->style) {
- return impl->style->getTransitionOptions();
- }
- return {};
+void Map::Impl::onSourceChanged(style::Source& source) {
+ observer.onSourceChanged(source);
}
-void Map::setTransitionOptions(const style::TransitionOptions& options) {
- if (impl->style) {
- impl->style->setTransitionOptions(options);
- }
+void Map::Impl::onInvalidate() {
+ onUpdate(Update::Repaint);
}
-bool Map::hasClass(const std::string& className) const {
- return impl->style ? impl->style->hasClass(className) : false;
-}
+void Map::Impl::onUpdate(Update flags) {
+ TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max();
-std::vector<std::string> Map::getClasses() const {
- if (impl->style) {
- return impl->style->getClasses();
- }
- return {};
-}
+ transform.updateTransitions(timePoint);
-void Map::setSourceTileCacheSize(size_t size) {
- if (size != impl->sourceCacheSize) {
- impl->sourceCacheSize = size;
- if (!impl->style) return;
- impl->style->setSourceTileCacheSize(size);
- impl->backend.invalidate();
+ if (flags & Update::AnnotationData) {
+ annotationManager.updateData();
}
-}
-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(),
+ scheduler,
+ fileSource,
+ 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 +747,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..4606e3eece 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -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..4d6b89573e 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -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
index 5e87515eac..057720a5c9 100644
--- a/src/mbgl/map/update.hpp
+++ b/src/mbgl/map/update.hpp
@@ -1,27 +1,25 @@
#pragma once
#include <mbgl/util/traits.hpp>
+#include <mbgl/util/util.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) {
+MBGL_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) {
+MBGL_CONSTEXPR Update& operator|=(Update& lhs, const Update& rhs) {
return (lhs = lhs | rhs);
}
-constexpr bool operator& (Update lhs, Update rhs) {
+MBGL_CONSTEXPR bool operator& (Update lhs, Update rhs) {
return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
}
diff --git a/src/mbgl/map/zoom_history.hpp b/src/mbgl/map/zoom_history.hpp
index 308846b1e3..ddec53e6af 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) {
+ 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 53eae49b77..3a38453d30 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, 3, 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 d18dc1e7eb..f9e91f569f 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) == 10, "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 0a56ae5dad..95b9362b85 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;
@@ -93,7 +91,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);
@@ -105,7 +103,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,
@@ -114,19 +111,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<
@@ -135,7 +134,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,
@@ -145,19 +143,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..692747bca4
--- /dev/null
+++ b/src/mbgl/renderer/image_manager.cpp
@@ -0,0 +1,177 @@
+#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()) {
+ 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, ImageDependencies dependencies) {
+ // If the sprite has been loaded, or if all the icon dependencies are already present
+ // (i.e. if they've been addeded via runtime styling), then notify the requestor immediately.
+ // Otherwise, delay notification until the sprite is loaded. At that point, if any of the
+ // dependencies are still unavailable, we'll just assume they are permanently missing.
+ bool hasAllDependencies = true;
+ if (!isLoaded()) {
+ for (const auto& dependency : dependencies) {
+ if (images.find(dependency) == images.end()) {
+ hasAllDependencies = false;
+ }
+ }
+ }
+ if (isLoaded() || hasAllDependencies) {
+ notify(requestor, dependencies);
+ } else {
+ requestors.emplace(&requestor, std::move(dependencies));
+ }
+}
+
+void ImageManager::removeRequestor(ImageRequestor& requestor) {
+ requestors.erase(&requestor);
+}
+
+void ImageManager::notify(ImageRequestor& requestor, const ImageDependencies& dependencies) const {
+ ImageMap response;
+
+ for (const auto& dependency : dependencies) {
+ auto it = images.find(dependency);
+ if (it != images.end()) {
+ response.emplace(*it);
+ }
+ }
+
+ requestor.onImagesAvailable(response);
+}
+
+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..1c9d67f47d
--- /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) = 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&, ImageDependencies);
+ void removeRequestor(ImageRequestor&);
+
+private:
+ void notify(ImageRequestor&, const ImageDependencies&) const;
+
+ bool loaded = false;
+
+ std::unordered_map<ImageRequestor*, ImageDependencies> 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..ae0c4b026b
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -0,0 +1,77 @@
+#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 && 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..d8e9d93811 100644
--- a/src/mbgl/renderer/render_custom_layer.hpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.hpp
@@ -1,25 +1,26 @@
#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* const impl;
+ const style::CustomLayer::Impl& impl() const;
+
+private:
+ bool initialized = false;
};
template <>
@@ -27,4 +28,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..6295f62b21
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -0,0 +1,179 @@
+#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::None;
+}
+
+bool RenderFillExtrusionLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ const auto size = parameters.context.viewport.getCurrentValue().size;
+
+ if (!parameters.staticData.extrusionTexture || parameters.staticData.extrusionTexture->getSize() != size) {
+ parameters.staticData.extrusionTexture = OffscreenTexture(parameters.context, size, OffscreenTextureAttachment::Depth);
+ }
+
+ parameters.staticData.extrusionTexture->bind();
+
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.setDepthMode(parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite));
+ parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {});
+
+ 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.depthModeForSublayer(0, 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.depthModeForSublayer(0, 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());
+ }
+ }
+
+ parameters.backend.bind();
+ parameters.context.bindTexture(parameters.staticData.extrusionTexture->getTexture());
+
+ 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..a53e00ca6f 100644
--- a/src/mbgl/renderer/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -1,22 +1,20 @@
#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>
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 +27,9 @@ public:
// Paint properties
style::FillExtrusionPaintProperties::Unevaluated unevaluated;
- style::FillExtrusionPaintProperties::Evaluated evaluated;
+ style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;
- const style::FillExtrusionLayer::Impl* const impl;
+ const style::FillExtrusionLayer::Impl& impl() const;
};
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..394642a50d
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -0,0 +1,203 @@
+#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, gl::DepthMode::ReadWrite),
+ *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..ebdaecd3a3
--- /dev/null
+++ b/src/mbgl/renderer/paint_parameters.cpp
@@ -0,0 +1,93 @@
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/render_style.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,
+ RenderStyle& style,
+ RenderStaticData& staticData_,
+ FrameHistory& frameHistory_)
+ : context(context_),
+ backend(backend_),
+ state(updateParameters.transformState),
+ evaluatedLight(style.getRenderLight().getEvaluated()),
+ staticData(staticData_),
+ frameHistory(frameHistory_),
+ imageManager(*style.imageManager),
+ lineAtlas(*style.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::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..e9d3562a75 100644
--- a/src/mbgl/renderer/paint_parameters.hpp
+++ b/src/mbgl/renderer/paint_parameters.hpp
@@ -1,14 +1,76 @@
#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 RenderStyle;
+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&,
+ RenderStyle&,
+ RenderStaticData&,
+ FrameHistory&);
+
+ 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::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 cad76ace4f..0000000000
--- a/src/mbgl/renderer/painter_fill.cpp
+++ /dev/null
@@ -1,142 +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, gl::DepthMode::ReadWrite),
- stencilModeForClipping(tile.clip),
- colorModeForRenderPass(),
- FillProgram::UniformValues {
- uniforms::u_matrix::Value{
- tile.translatedMatrix(properties.get<FillTranslate>(),
- properties.get<FillTranslateAnchor>(),
- state)
- },
- uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
- },
- *bucket.vertexBuffer,
- indexBuffer,
- segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom(),
- 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
index 787211c30a..4bf5629263 100644
--- a/src/mbgl/renderer/render_item.hpp
+++ b/src/mbgl/renderer/render_item.hpp
@@ -17,13 +17,13 @@ namespace style {
class RenderItem {
public:
- RenderItem(const RenderLayer& layer_,
- std::vector<std::reference_wrapper<RenderTile>> tiles_ = {})
- : layer(layer_), tiles(std::move(tiles_)) {
+ RenderItem(RenderLayer& layer_,
+ RenderSource* renderSource_)
+ : layer(layer_), source(renderSource_) {
}
- const RenderLayer& layer;
- std::vector<std::reference_wrapper<RenderTile>> tiles;
+ RenderLayer& layer;
+ RenderSource* source;
};
class RenderData {
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..ae2b923ba1 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>
@@ -12,15 +13,15 @@ enum class RenderPass : uint8_t {
Translucent = 1 << 1,
};
-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..b565439588 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,74 @@
namespace mbgl {
-class Painter;
+class PaintParameters;
class TransformState;
class RenderTile;
+class RenderStyle;
+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 RenderStyle& style,
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..07a47b4c8f
--- /dev/null
+++ b/src/mbgl/renderer/render_static_data.hpp
@@ -0,0 +1,38 @@
+#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 <mbgl/util/offscreen_texture.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<OffscreenTexture> extrusionTexture;
+
+ Programs programs;
+
+#ifndef NDEBUG
+ Programs overdrawPrograms;
+#endif
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style.cpp b/src/mbgl/renderer/render_style.cpp
new file mode 100644
index 0000000000..3d95b12bc4
--- /dev/null
+++ b/src/mbgl/renderer/render_style.cpp
@@ -0,0 +1,449 @@
+#include <mbgl/renderer/render_style.hpp>
+#include <mbgl/renderer/render_style_observer.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/render_item.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/layers/render_background_layer.hpp>
+#include <mbgl/renderer/layers/render_circle_layer.hpp>
+#include <mbgl/renderer/layers/render_custom_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_layer.hpp>
+#include <mbgl/renderer/layers/render_line_layer.hpp>
+#include <mbgl/renderer/layers/render_raster_layer.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/style_diff.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/renderer/query.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/sprite/sprite_loader.hpp>
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/logging.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderStyleObserver nullObserver;
+
+RenderStyle::RenderStyle(Scheduler& scheduler_, FileSource& fileSource_)
+ : scheduler(scheduler_),
+ fileSource(fileSource_),
+ glyphManager(std::make_unique<GlyphManager>(fileSource)),
+ imageManager(std::make_unique<ImageManager>()),
+ lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
+ imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>()),
+ sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()),
+ layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()),
+ renderLight(makeMutable<Light::Impl>()),
+ observer(&nullObserver) {
+ glyphManager->setObserver(this);
+}
+
+RenderStyle::~RenderStyle() {
+ assert(BackendScope::exists()); // Required for custom layers.
+}
+
+void RenderStyle::setObserver(RenderStyleObserver* observer_) {
+ observer = observer_;
+}
+
+std::vector<const RenderLayer*> RenderStyle::getRenderLayers() const {
+ std::vector<const RenderLayer*> result;
+ result.reserve(renderLayers.size());
+ for (const auto& layer : *layerImpls) {
+ result.push_back(getRenderLayer(layer->id));
+ }
+ return result;
+}
+
+RenderLayer* RenderStyle::getRenderLayer(const std::string& id) {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+const RenderLayer* RenderStyle::getRenderLayer(const std::string& id) const {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+const RenderLight& RenderStyle::getRenderLight() const {
+ return renderLight;
+}
+
+void RenderStyle::update(const UpdateParameters& parameters) {
+ assert(BackendScope::exists()); // Required for custom layers.
+
+ const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint);
+
+ const TransitionParameters transitionParameters {
+ parameters.timePoint,
+ parameters.mode == MapMode::Continuous ? parameters.transitionOptions : TransitionOptions()
+ };
+
+ const PropertyEvaluationParameters evaluationParameters {
+ zoomHistory,
+ parameters.timePoint,
+ parameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero()
+ };
+
+ const TileParameters tileParameters {
+ parameters.pixelRatio,
+ parameters.debugOptions,
+ parameters.transformState,
+ parameters.scheduler,
+ parameters.fileSource,
+ parameters.mode,
+ parameters.annotationManager,
+ *imageManager,
+ *glyphManager,
+ parameters.prefetchZoomDelta
+ };
+
+ glyphManager->setURL(parameters.glyphURL);
+
+ // Update light.
+ const bool lightChanged = renderLight.impl != parameters.light;
+
+ if (lightChanged) {
+ renderLight.impl = parameters.light;
+ renderLight.transition(transitionParameters);
+ }
+
+ if (lightChanged || zoomChanged || renderLight.hasTransition()) {
+ renderLight.evaluate(evaluationParameters);
+ }
+
+
+ const ImageDifference imageDiff = diffImages(imageImpls, parameters.images);
+ imageImpls = parameters.images;
+
+ // Remove removed images from sprite atlas.
+ for (const auto& entry : imageDiff.removed) {
+ imageManager->removeImage(entry.first);
+ }
+
+ // Add added images to sprite atlas.
+ for (const auto& entry : imageDiff.added) {
+ imageManager->addImage(entry.second);
+ }
+
+ // Update changed images.
+ for (const auto& entry : imageDiff.changed) {
+ imageManager->updateImage(entry.second.after);
+ }
+
+ imageManager->setLoaded(parameters.spriteLoaded);
+
+
+ const LayerDifference layerDiff = diffLayers(layerImpls, parameters.layers);
+ layerImpls = parameters.layers;
+
+ // Remove render layers for removed layers.
+ for (const auto& entry : layerDiff.removed) {
+ renderLayers.erase(entry.first);
+ }
+
+ // Create render layers for newly added layers.
+ for (const auto& entry : layerDiff.added) {
+ renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
+ }
+
+ // Update render layers for changed layers.
+ for (const auto& entry : layerDiff.changed) {
+ renderLayers.at(entry.first)->setImpl(entry.second.after);
+ }
+
+ // Update layers for class and zoom changes.
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ const bool layerAdded = layerDiff.added.count(entry.first);
+ const bool layerChanged = layerDiff.changed.count(entry.first);
+
+ if (layerAdded || layerChanged) {
+ layer.transition(transitionParameters);
+ }
+
+ if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
+ layer.evaluate(evaluationParameters);
+ }
+ }
+
+
+ const SourceDifference sourceDiff = diffSources(sourceImpls, parameters.sources);
+ sourceImpls = parameters.sources;
+
+ // Remove render layers for removed sources.
+ for (const auto& entry : sourceDiff.removed) {
+ renderSources.erase(entry.first);
+ }
+
+ // Create render sources for newly added sources.
+ for (const auto& entry : sourceDiff.added) {
+ std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
+ renderSource->setObserver(this);
+ renderSources.emplace(entry.first, std::move(renderSource));
+ }
+
+ // Update all sources.
+ for (const auto& source : *sourceImpls) {
+ std::vector<Immutable<Layer::Impl>> filteredLayers;
+ bool needsRendering = false;
+ bool needsRelayout = false;
+
+ for (const auto& layer : *layerImpls) {
+ if (layer->type == LayerType::Background ||
+ layer->type == LayerType::Custom ||
+ layer->source != source->id) {
+ continue;
+ }
+
+ if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
+ needsRendering = true;
+ }
+
+ if (!needsRelayout && (
+ hasLayoutDifference(layerDiff, layer->id) ||
+ !imageDiff.added.empty() ||
+ !imageDiff.removed.empty() ||
+ !imageDiff.changed.empty())) {
+ needsRelayout = true;
+ }
+
+ filteredLayers.push_back(layer);
+ }
+
+ renderSources.at(source->id)->update(source,
+ filteredLayers,
+ needsRendering,
+ needsRelayout,
+ tileParameters);
+ }
+}
+
+RenderSource* RenderStyle::getRenderSource(const std::string& id) const {
+ auto it = renderSources.find(id);
+ return it != renderSources.end() ? it->second.get() : nullptr;
+}
+
+bool RenderStyle::hasTransitions() const {
+ if (renderLight.hasTransition()) {
+ return true;
+ }
+
+ for (const auto& entry : renderLayers) {
+ if (entry.second->hasTransition()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool RenderStyle::isLoaded() const {
+ for (const auto& entry: renderSources) {
+ if (!entry.second->isLoaded()) {
+ return false;
+ }
+ }
+
+ if (!imageManager->isLoaded()) {
+ return false;
+ }
+
+ return true;
+}
+
+RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle) {
+ RenderData result;
+
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ result.sources.insert(entry.second.get());
+ }
+ }
+
+ for (auto& layerImpl : *layerImpls) {
+ RenderLayer* layer = getRenderLayer(layerImpl->id);
+ assert(layer);
+
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+
+ if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
+ if (debugOptions & MapDebugOptions::Overdraw) {
+ // We want to skip glClear optimization in overdraw mode.
+ result.order.emplace_back(*layer, nullptr);
+ continue;
+ }
+ const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
+ if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) {
+ // This is a solid background. We can use glClear().
+ result.backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
+ } else {
+ // This is a textured background, or not the bottommost layer. We need to render it with a quad.
+ result.order.emplace_back(*layer, nullptr);
+ }
+ continue;
+ }
+
+ if (layer->is<RenderCustomLayer>()) {
+ result.order.emplace_back(*layer, nullptr);
+ continue;
+ }
+
+ RenderSource* source = getRenderSource(layer->baseImpl->source);
+ if (!source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
+ continue;
+ }
+
+ 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(),
+ [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);
+ });
+ } 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));
+ result.order.emplace_back(*layer, source);
+ }
+
+ return result;
+}
+
+std::vector<Feature> RenderStyle::queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const RenderedQueryOptions& options) const {
+ std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
+
+ if (options.layerIDs) {
+ std::unordered_set<std::string> sourceIDs;
+ for (const auto& layerID : *options.layerIDs) {
+ if (const RenderLayer* layer = getRenderLayer(layerID)) {
+ sourceIDs.emplace(layer->baseImpl->source);
+ }
+ }
+ for (const auto& sourceID : sourceIDs) {
+ if (RenderSource* renderSource = getRenderSource(sourceID)) {
+ auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, *this, options);
+ std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
+ }
+ }
+ } else {
+ for (const auto& entry : renderSources) {
+ auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, *this, options);
+ std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
+ }
+ }
+
+ std::vector<Feature> result;
+
+ if (resultsByLayer.empty()) {
+ return result;
+ }
+
+ // Combine all results based on the style layer order.
+ for (const auto& layerImpl : *layerImpls) {
+ const RenderLayer* layer = getRenderLayer(layerImpl->id);
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+ auto it = resultsByLayer.find(layer->baseImpl->id);
+ if (it != resultsByLayer.end()) {
+ std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
+ }
+ }
+
+ return result;
+}
+
+void RenderStyle::onLowMemory() {
+ for (const auto& entry : renderSources) {
+ entry.second->onLowMemory();
+ }
+}
+
+void RenderStyle::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
+ glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void RenderStyle::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
+ util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) {
+ observer->onInvalidate();
+}
+
+void RenderStyle::dumpDebugLogs() const {
+ for (const auto& entry : renderSources) {
+ entry.second->dumpDebugLogs();
+ }
+
+ imageManager->dumpDebugLogs();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style.hpp b/src/mbgl/renderer/render_style.hpp
new file mode 100644
index 0000000000..23a640c482
--- /dev/null
+++ b/src/mbgl/renderer/render_style.hpp
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <mbgl/style/image.hpp>
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/render_source_observer.hpp>
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/renderer/render_light.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/map/zoom_history.hpp>
+#include <mbgl/map/mode.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+class FileSource;
+class GlyphManager;
+class ImageManager;
+class LineAtlas;
+class RenderData;
+class TransformState;
+class RenderedQueryOptions;
+class Scheduler;
+class UpdateParameters;
+class RenderStyleObserver;
+
+namespace style {
+class Image;
+class Source;
+class Layer;
+} // namespace style
+
+class RenderStyle : public GlyphManagerObserver,
+ public RenderSourceObserver {
+public:
+ RenderStyle(Scheduler&, FileSource&);
+ ~RenderStyle() final;
+
+ void setObserver(RenderStyleObserver*);
+ void update(const UpdateParameters&);
+
+ bool isLoaded() const;
+ bool hasTransitions() const;
+
+ RenderSource* getRenderSource(const std::string& id) const;
+
+ std::vector<const RenderLayer*> getRenderLayers() const;
+
+ RenderLayer* getRenderLayer(const std::string& id);
+ const RenderLayer* getRenderLayer(const std::string& id) const;
+
+ const RenderLight& getRenderLight() const;
+
+ RenderData getRenderData(MapDebugOptions, float angle);
+
+ std::vector<Feature> queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const RenderedQueryOptions& options) const;
+
+ void onLowMemory();
+
+ void dumpDebugLogs() const;
+
+ Scheduler& scheduler;
+ FileSource& fileSource;
+ std::unique_ptr<GlyphManager> glyphManager;
+ std::unique_ptr<ImageManager> imageManager;
+ std::unique_ptr<LineAtlas> lineAtlas;
+
+private:
+ Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;
+ Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls;
+ Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls;
+
+ std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources;
+ std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
+ RenderLight renderLight;
+
+ // GlyphManagerObserver implementation.
+ void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
+
+ // RenderSourceObserver implementation.
+ void onTileChanged(RenderSource&, const OverscaledTileID&) override;
+ void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
+
+ RenderStyleObserver* observer;
+ ZoomHistory zoomHistory;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style_observer.hpp b/src/mbgl/renderer/render_style_observer.hpp
new file mode 100644
index 0000000000..5852dd68b8
--- /dev/null
+++ b/src/mbgl/renderer/render_style_observer.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <exception>
+
+namespace mbgl {
+
+class RenderStyleObserver {
+public:
+ virtual ~RenderStyleObserver() = default;
+ virtual void onInvalidate() {}
+ virtual void onResourceError(std::exception_ptr) {}
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_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..9f4b897d6e
--- /dev/null
+++ b/src/mbgl/renderer/renderer.cpp
@@ -0,0 +1,78 @@
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_impl.hpp>
+#include <mbgl/renderer/update_parameters.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() = default;
+
+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() {
+ 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..dd3c0d41a1
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -0,0 +1,361 @@
+#include <mbgl/renderer/renderer_impl.hpp>
+#include <mbgl/renderer/render_style.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/renderer/render_item.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/gl/debugging.hpp>
+#include <mbgl/geometry/line_atlas.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_)
+ , observer(&nullObserver())
+ , contextMode(contextMode_)
+ , pixelRatio(pixelRatio_)
+ , programCacheDir(programCacheDir_)
+ , renderStyle(std::make_unique<RenderStyle>(scheduler_, fileSource_)) {
+
+ renderStyle->setObserver(this);
+}
+
+Renderer::Impl::~Impl() {
+ BackendScope guard { backend };
+ renderStyle.reset();
+ staticData.reset();
+};
+
+void Renderer::Impl::setObserver(RendererObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver();
+}
+
+void Renderer::Impl::render(const UpdateParameters& updateParameters) {
+ // Don't load/render anyting in still mode until explicitly requested.
+ if (updateParameters.mode == MapMode::Still && !updateParameters.stillImageRequest) return;
+
+ assert(BackendScope::exists());
+
+ renderStyle->update(updateParameters);
+ transformState = updateParameters.transformState;
+
+ if (!staticData) {
+ staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio, programCacheDir);
+ }
+
+ PaintParameters parameters {
+ backend.getContext(),
+ pixelRatio,
+ contextMode,
+ backend,
+ updateParameters,
+ *renderStyle,
+ *staticData,
+ frameHistory
+ };
+
+ bool loaded = updateParameters.styleLoaded && renderStyle->isLoaded();
+
+ if (updateParameters.mode == MapMode::Continuous) {
+ if (renderState == RenderState::Never) {
+ observer->onWillStartRenderingMap();
+ }
+
+ observer->onWillStartRenderingFrame();
+
+ backend.updateAssumedState();
+
+ doRender(parameters);
+ parameters.context.performCleanup();
+
+ observer->onDidFinishRenderingFrame(
+ loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
+ renderStyle->hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION)
+ );
+
+ if (!loaded) {
+ renderState = RenderState::Partial;
+ } else if (renderState != RenderState::Fully) {
+ renderState = RenderState::Fully;
+ observer->onDidFinishRenderingMap();
+ }
+ } else if (loaded) {
+ observer->onWillStartRenderingMap();
+ observer->onWillStartRenderingFrame();
+
+ backend.updateAssumedState();
+
+ doRender(parameters);
+
+ observer->onDidFinishRenderingFrame(RendererObserver::RenderMode::Full, false);
+ observer->onDidFinishRenderingMap();
+
+ // Cleanup only after signaling completion
+ parameters.context.performCleanup();
+ }
+}
+
+void Renderer::Impl::doRender(PaintParameters& parameters) {
+ if (parameters.contextMode == GLContextMode::Shared) {
+ parameters.context.setDirtyState();
+ }
+
+ RenderData renderData = renderStyle->getRenderData(parameters.debugOptions, parameters.state.getAngle());
+ const std::vector<RenderItem>& order = renderData.order;
+ const std::unordered_set<RenderSource*>& sources = renderData.sources;
+
+ 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);
+ }
+
+ // - 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()
+ : renderData.backgroundColor,
+ 1.0f,
+ 0);
+ }
+
+ // - CLIPPING MASKS ----------------------------------------------------------------------------
+ // Draws the clipping masks to the stencil buffer.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "clip");
+
+ // Update all clipping IDs.
+ for (const auto& source : sources) {
+ source->startRender(parameters);
+ }
+
+ 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
+
+ int indent = 0;
+
+ // Actually render the layers
+ if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
+
+ 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");
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", "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);
+ }
+ }
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ }
+ }
+
+ // - 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");
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", "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);
+ }
+ }
+
+ if (debug::renderTree) {
+ Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
+ }
+ }
+
+ if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; }
+
+ // - 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& source : sources) {
+ source->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.activeTexture = 1;
+ parameters.context.texture[1] = 0;
+ parameters.context.activeTexture = 0;
+ parameters.context.texture[0] = 0;
+
+ parameters.context.bindVertexArray = 0;
+ }
+}
+
+std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const {
+ return renderStyle->queryRenderedFeatures(geometry, transformState, options);
+}
+
+std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const {
+ const RenderSource* source = renderStyle->getRenderSource(sourceID);
+ if (!source) return {};
+
+ return source->querySourceFeatures(options);
+}
+
+void Renderer::Impl::onInvalidate() {
+ observer->onInvalidate();
+}
+
+void Renderer::Impl::onResourceError(std::exception_ptr ptr) {
+ observer->onResourceError(ptr);
+}
+
+void Renderer::Impl::onLowMemory() {
+ BackendScope guard { backend };
+ backend.getContext().performCleanup();
+ renderStyle->onLowMemory();
+ observer->onInvalidate();
+}
+
+void Renderer::Impl::dumDebugLogs() {
+ renderStyle->dumpDebugLogs();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp
new file mode 100644
index 0000000000..079b00d0bb
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/renderer/render_style_observer.hpp>
+#include <mbgl/renderer/frame_history.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+class UpdateParameters;
+class PaintParameters;
+class RenderStyle;
+class RenderStaticData;
+
+class Renderer::Impl : public RenderStyleObserver {
+public:
+ Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode,
+ const optional<std::string> programCacheDir);
+ ~Impl() final;
+
+ 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();
+
+ // RenderStyleObserver implementation
+ void onInvalidate() override;
+ void onResourceError(std::exception_ptr) override;
+
+private:
+ void doRender(PaintParameters&);
+
+ friend class Renderer;
+
+ RendererBackend& backend;
+ 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;
+ TransformState transformState;
+
+ std::unique_ptr<RenderStyle> renderStyle;
+ std::unique_ptr<RenderStaticData> staticData;
+};
+
+} // 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..df8bcc0ae7 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -1,5 +1,6 @@
#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/algorithm/generate_clip_ids.hpp>
@@ -9,35 +10,29 @@ 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();
+const style::GeoJSONSource::Impl& RenderGeoJSONSource::impl() const {
+ return static_cast<const style::GeoJSONSource::Impl&>(*baseImpl);
}
-void RenderGeoJSONSource::startRender(algorithm::ClipIDGenerator& generator, const mat4& projMatrix, const mat4& clipMatrix, const TransformState& transform) {
- generator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(projMatrix, clipMatrix, transform);
+bool RenderGeoJSONSource::isLoaded() const {
+ return tilePyramid.isLoaded();
}
-void RenderGeoJSONSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
-}
+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_);
-std::map<UnwrappedTileID, RenderTile>& RenderGeoJSONSource::getRenderTiles() {
- return tilePyramid.getRenderTiles();
-}
+ enabled = needsRendering;
-void RenderGeoJSONSource::updateTiles(const TileParameters& parameters) {
- GeoJSONData* data_ = impl.getData();
+ GeoJSONData* data_ = impl().getData();
if (!data_) {
return;
@@ -52,38 +47,43 @@ void RenderGeoJSONSource::updateTiles(const TileParameters& parameters) {
}
}
- 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));
- });
+ 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 RenderStyle& style,
+ const RenderedQueryOptions& options) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, style, 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..b84156ab95 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 RenderStyle& style,
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..50d5b17ec2
--- /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 RenderStyle&,
+ 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..fc1a462090
--- /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 RenderStyle& style,
+ 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..cbd874f647 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 RenderStyle&,
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..1f4678da9f 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 RenderStyle& style,
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..47bccdaca8 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 RenderStyle& style,
const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, options);
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, style, 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..256ad4e800 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 RenderStyle& style,
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..219f154675 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);
@@ -134,11 +173,13 @@ void TilePyramid::updateTiles(const TileParameters& parameters,
removeStaleTiles(retain);
- const PlacementConfig config { parameters.transformState.getAngle(),
- parameters.transformState.getPitch(),
- parameters.debugOptions & MapDebugOptions::Collision };
-
for (auto& pair : tiles) {
+ const PlacementConfig config { parameters.transformState.getAngle(),
+ parameters.transformState.getPitch(),
+ parameters.transformState.getCameraToCenterDistance(),
+ parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()),
+ parameters.debugOptions & MapDebugOptions::Collision };
+
pair.second->setPlacementConfig(config);
}
}
@@ -163,23 +204,9 @@ void TilePyramid::removeStaleTiles(const std::set<OverscaledTileID>& retain) {
}
}
-void TilePyramid::removeTiles() {
- renderTiles.clear();
- if (!tiles.empty()) {
- removeStaleTiles({});
- }
-}
-
-void TilePyramid::reloadTiles() {
- cache.clear();
-
- for (auto& pair : tiles) {
- pair.second->redoLayout();
- }
-}
-
std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
+ const RenderStyle& style,
const RenderedQueryOptions& options) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
@@ -195,18 +222,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 +249,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
renderTile.tile.queryRenderedFeatures(result,
tileSpaceQueryGeometry,
transformState,
+ style,
options);
}
diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp
index b51c5342de..d940f378fa 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 RenderStyle;
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 RenderStyle& style,
const RenderedQueryOptions& options) const;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const;
@@ -76,7 +68,7 @@ public:
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..181b11784d 100644
--- a/src/mbgl/renderer/update_parameters.hpp
+++ b/src/mbgl/renderer/update_parameters.hpp
@@ -1,26 +1,47 @@
#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;
+ 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;
+
Scheduler& scheduler;
FileSource& fileSource;
AnnotationManager& annotationManager;
+
+ const uint8_t prefetchZoomDelta;
+
+ // For still image requests, render requested
+ const bool stillImageRequest;
};
} // namespace mbgl
diff --git a/src/mbgl/shaders/circle.cpp b/src/mbgl/shaders/circle.cpp
index 2e0c76122c..c14335914b 100644
--- a/src/mbgl/shaders/circle.cpp
+++ b/src/mbgl/shaders/circle.cpp
@@ -9,7 +9,9 @@ const char* circle::name = "circle";
const char* circle::vertexSource = R"MBGL_SHADER(
uniform mat4 u_matrix;
uniform bool u_scale_with_map;
+uniform bool u_pitch_with_map;
uniform vec2 u_extrude_scale;
+uniform highp float u_camera_to_center_distance;
attribute vec2 a_pos;
@@ -22,6 +24,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
uniform lowp float a_radius_t;
attribute mediump vec2 a_radius;
@@ -30,6 +33,7 @@ varying mediump float radius;
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -38,6 +42,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -46,6 +51,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
uniform lowp float a_stroke_color_t;
attribute highp vec4 a_stroke_color;
@@ -54,6 +60,7 @@ varying highp vec4 stroke_color;
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
uniform lowp float a_stroke_width_t;
attribute mediump vec2 a_stroke_width;
@@ -62,6 +69,7 @@ varying mediump float stroke_width;
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
uniform lowp float a_stroke_opacity_t;
attribute lowp vec2 a_stroke_opacity;
@@ -70,63 +78,87 @@ varying lowp float stroke_opacity;
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main(void) {
-
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
radius = unpack_mix_vec2(a_radius, a_radius_t);
#else
mediump float radius = u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
stroke_color = unpack_mix_vec4(a_stroke_color, a_stroke_color_t);
#else
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
stroke_width = unpack_mix_vec2(a_stroke_width, a_stroke_width_t);
#else
mediump float stroke_width = u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
stroke_opacity = unpack_mix_vec2(a_stroke_opacity, a_stroke_opacity_t);
#else
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
// unencode the extrusion vector that we snuck into the a_pos vector
vec2 extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);
// multiply a_pos by 0.5, since we had it * 2 in order to sneak
// in extrusion data
- gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0, 1);
-
- if (u_scale_with_map) {
- gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale;
+ vec2 circle_center = floor(a_pos * 0.5);
+ if (u_pitch_with_map) {
+ vec2 corner_position = circle_center;
+ if (u_scale_with_map) {
+ corner_position += extrude * (radius + stroke_width) * u_extrude_scale;
+ } else {
+ // Pitching the circle with the map effectively scales it with the map
+ // To counteract the effect for pitch-scale: viewport, we rescale the
+ // whole circle based on the pitch scaling effect at its central point
+ vec4 projected_center = u_matrix * vec4(circle_center, 0, 1);
+ corner_position += extrude * (radius + stroke_width) * u_extrude_scale * (projected_center.w / u_camera_to_center_distance);
+ }
+
+ gl_Position = u_matrix * vec4(corner_position, 0, 1);
} else {
- gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w;
+ gl_Position = u_matrix * vec4(circle_center, 0, 1);
+
+ if (u_scale_with_map) {
+ gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * u_camera_to_center_distance;
+ } else {
+ gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w;
+ }
}
// This is a minimum blur distance that serves as a faux-antialiasing for
@@ -146,74 +178,88 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
varying mediump float radius;
#else
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
varying highp vec4 stroke_color;
#else
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
varying mediump float stroke_width;
#else
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
varying lowp float stroke_opacity;
#else
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main() {
-
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_radius
mediump float radius = u_radius;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_color
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_width
mediump float stroke_width = u_stroke_width;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_opacity
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
vec2 extrude = v_data.xy;
float extrude_length = length(extrude);
diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp
index 5f733c6a1e..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 ce62ecb3bd..f68cc91377 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;
@@ -120,11 +144,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.
@@ -158,36 +182,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 f43b3a3cf7..f1e64577e2 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;
@@ -110,11 +132,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.
@@ -148,6 +170,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;
@@ -164,22 +187,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;
@@ -193,8 +220,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 24b7153c7f..dd81433543 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;
@@ -129,11 +170,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,8 +196,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);
}
@@ -181,32 +222,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;
@@ -219,7 +290,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..1a30a1f1c7 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<TextAnchorType>;
+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..7815f4aca0 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<TextAnchorType> IdentityStops<TextAnchorType>::evaluate(const Value& value) const {
+ if (!value.is<std::string>()) {
+ return {};
+ }
+
+ return Enum<TextAnchorType>::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..75dc83206c
--- /dev/null
+++ b/src/mbgl/style/image_impl.hpp
@@ -0,0 +1,32 @@
+#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>;
+
+} // 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..e37382d5ef 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 {
@@ -10,21 +10,52 @@ CustomLayer::CustomLayer(const std::string& layerID,
CustomLayerRenderFunction render,
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, deinit, context)) {
}
-CustomLayer::CustomLayer(const Impl& other)
- : Layer(LayerType::Custom, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
+CustomLayer::~CustomLayer() = default;
+
+const CustomLayer::Impl& CustomLayer::impl() const {
+ return static_cast<const Impl&>(*baseImpl);
}
-CustomLayer::~CustomLayer() = default;
+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..42e60c582c 100644
--- a/src/mbgl/style/layers/custom_layer_impl.cpp
+++ b/src/mbgl/style/layers/custom_layer_impl.cpp
@@ -1,74 +1,26 @@
#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_,
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_;
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..defbbe6894 100644
--- a/src/mbgl/style/layers/custom_layer_impl.hpp
+++ b/src/mbgl/style/layers/custom_layer_impl.hpp
@@ -17,20 +17,9 @@ public:
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;
CustomLayerDeinitializeFunction deinitializeFn = 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..803ae7397e 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,560 @@ 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);
+}
+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() {
return TextMaxWidth::defaultValue();
}
PropertyValue<float> SymbolLayer::getTextMaxWidth() const {
- return impl->layout.unevaluated.get<TextMaxWidth>();
+ return impl().layout.get<TextMaxWidth>();
}
void SymbolLayer::setTextMaxWidth(PropertyValue<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() {
return TextLetterSpacing::defaultValue();
}
PropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
- return impl->layout.unevaluated.get<TextLetterSpacing>();
+ return impl().layout.get<TextLetterSpacing>();
}
void SymbolLayer::setTextLetterSpacing(PropertyValue<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<TextAnchorType> SymbolLayer::getDefaultTextAnchor() {
return TextAnchor::defaultValue();
}
-PropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const {
- return impl->layout.unevaluated.get<TextAnchor>();
+DataDrivenPropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const {
+ return impl().layout.get<TextAnchor>();
}
-void SymbolLayer::setTextAnchor(PropertyValue<TextAnchorType> value) {
+void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<TextAnchorType> 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 +659,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..fe6ab38e92 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,11 @@ struct IconOffset : DataDrivenLayoutProperty<std::array<float, 2>> {
static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; }
};
+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; }
@@ -126,12 +132,12 @@ struct TextLetterSpacing : LayoutProperty<float> {
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<TextAnchorType> {
static constexpr const char * key = "text-anchor";
static TextAnchorType defaultValue() { return TextAnchorType::Center; }
};
@@ -237,7 +243,7 @@ struct TextTranslateAnchor : PaintProperty<TranslateAnchorType> {
static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; }
};
-class SymbolLayoutProperties : public LayoutProperties<
+class SymbolLayoutProperties : public Properties<
SymbolPlacement,
SymbolSpacing,
SymbolAvoidEdges,
@@ -253,6 +259,7 @@ class SymbolLayoutProperties : public LayoutProperties<
IconPadding,
IconKeepUpright,
IconOffset,
+ IconPitchAlignment,
TextPitchAlignment,
TextRotationAlignment,
TextField,
@@ -274,7 +281,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..ea19c599e9 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -1,7 +1,5 @@
#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>
@@ -10,13 +8,12 @@
namespace mbgl {
namespace style {
-class Observer : public GlyphAtlasObserver,
- public SpriteAtlasObserver,
- public SourceObserver {
+class Observer : public SourceObserver {
public:
+ virtual void onStyleLoading() {}
+ virtual void onStyleLoaded() {}
virtual void onUpdate(Update) {}
virtual void onStyleError(std::exception_ptr) {}
- virtual void onStyleLoaded() {}
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..0fb49d1d22
--- /dev/null
+++ b/src/mbgl/style/style_impl.cpp
@@ -0,0 +1,363 @@
+#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 = true;
+ 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;
+
+ 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(Update::Repaint);
+
+ 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(Update::Repaint);
+ }
+
+ 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(Update::Repaint);
+}
+
+void Style::Impl::onSourceChanged(Source& source) {
+ sources.update(source);
+ observer->onSourceChanged(source);
+ observer->onUpdate(Update::Repaint);
+}
+
+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(Update::Repaint); // 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(Update::Repaint);
+}
+
+void Style::Impl::onLightChanged(const Light&) {
+ observer->onUpdate(Update::Repaint);
+}
+
+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..4fbf767e11 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, {
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 ea4324edaf..9868266aa2 100644
--- a/src/mbgl/text/collision_tile.hpp
+++ b/src/mbgl/text/collision_tile.hpp
@@ -16,7 +16,10 @@
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+#ifndef __clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#pragma GCC diagnostic ignored "-Wmisleading-indentation"
+#endif
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
@@ -28,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;
@@ -46,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;
@@ -55,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..1e1279341d 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 &&
+ cameraToCenterDistance == rhs.cameraToCenterDistance &&
+ (pitch * util::RAD2DEG < 25 || cameraToTileDistance == rhs.cameraToTileDistance) &&
+ debug == rhs.debug;
}
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..a40ef0cf39 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -7,22 +7,19 @@
#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 {};
- }
-
+PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
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] / 2.0f;
+ float x2 = x1 + image.displaySize()[0];
+ float y1 = dy - image.displaySize()[1] / 2.0f;
+ 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 +28,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 +40,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 +51,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 +63,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 +162,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 +180,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 +200,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::TextAnchorType 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 +213,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 +234,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,11 +257,48 @@ void shapeLines(Shaping& shaping,
x = 0;
y += lineHeight;
}
-
- align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight,
- lines.size(), translate);
+
+ float horizontalAlign = 0.5;
+ float verticalAlign = 0.5;
+
+ switch (textAnchor) {
+ case style::TextAnchorType::Top:
+ case style::TextAnchorType::Bottom:
+ case style::TextAnchorType::Center:
+ break;
+ case style::TextAnchorType::Right:
+ case style::TextAnchorType::TopRight:
+ case style::TextAnchorType::BottomRight:
+ horizontalAlign = 1;
+ break;
+ case style::TextAnchorType::Left:
+ case style::TextAnchorType::TopLeft:
+ case style::TextAnchorType::BottomLeft:
+ horizontalAlign = 0;
+ break;
+ }
+
+ switch (textAnchor) {
+ case style::TextAnchorType::Left:
+ case style::TextAnchorType::Right:
+ case style::TextAnchorType::Center:
+ break;
+ case style::TextAnchorType::Bottom:
+ case style::TextAnchorType::BottomLeft:
+ case style::TextAnchorType::BottomRight:
+ verticalAlign = 1;
+ break;
+ case style::TextAnchorType::Top:
+ case style::TextAnchorType::TopLeft:
+ case style::TextAnchorType::TopRight:
+ verticalAlign = 0;
+ break;
+ }
+
+ align(shaping, justify, horizontalAlign, verticalAlign,
+ maxLineLength, lineHeight, lines.size());
const uint32_t height = lines.size() * lineHeight;
-
+
// Calculate the bounding box
shaping.top += -verticalAlign * height;
shaping.bottom = shaping.top + height;
@@ -276,23 +309,22 @@ void shapeLines(Shaping& shaping,
const Shaping getShaping(const std::u16string& logicalInput,
const float maxWidth,
const float lineHeight,
- const float horizontalAlign,
- const float verticalAlign,
- const float justify,
+ const style::TextAnchorType 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..00e4ec55f8 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,9 @@ 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, 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 +45,13 @@ public:
const Shaping getShaping(const std::u16string& string,
float maxWidth,
float lineHeight,
- float horizontalAlign,
- float verticalAlign,
- float justify,
+ style::TextAnchorType 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 29ba7d42cd..20056e355d 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -6,20 +6,21 @@
#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>
@@ -29,30 +30,33 @@ using namespace style;
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),
+ placementThrottler(Milliseconds(300), [this] { invokePlacement(); }),
+ lastYStretch(1.0f) {
}
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;
}
@@ -69,7 +73,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) {
@@ -92,29 +95,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) {
@@ -134,10 +137,16 @@ void GeometryTile::onPlacement(PlacementResult result) {
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);
}
@@ -148,25 +157,51 @@ void GeometryTile::onError(std::exception_ptr err) {
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) {
+ worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images));
}
-void GeometryTile::getIcons(IconDependencies) {
- spriteAtlas.getIcons(*this);
+void GeometryTile::getImages(ImageDependencies imageDependencies) {
+ imageManager.getImages(*this, std::move(imageDependencies));
}
-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;
}
@@ -179,6 +214,7 @@ void GeometryTile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState& transformState,
+ const RenderStyle& style,
const RenderedQueryOptions& options) {
if (!featureIndex || !data) return;
@@ -232,4 +268,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 ed5d8d87bf..c45762742b 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,18 @@
namespace mbgl {
class GeometryTileData;
-class FeatureIndex;
-class CollisionTile;
+class RenderStyle;
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 +38,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) override;
void getGlyphs(GlyphDependencies);
- void getIcons(IconDependencies);
+ void getImages(ImageDependencies);
+
+ 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 RenderStyle&,
const RenderedQueryOptions& options) override;
void querySourceFeatures(
@@ -69,6 +71,15 @@ public:
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<GeometryTileData> tileData;
uint64_t correlationID;
+
+ LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_,
+ std::unique_ptr<FeatureIndex> featureIndex_,
+ std::unique_ptr<GeometryTileData> tileData_,
+ uint64_t correlationID_)
+ : nonSymbolBuckets(std::move(nonSymbolBuckets_)),
+ featureIndex(std::move(featureIndex_)),
+ tileData(std::move(tileData_)),
+ correlationID(correlationID_) {}
};
void onLayout(LayoutResult);
@@ -76,22 +87,37 @@ public:
public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
std::unique_ptr<CollisionTile> collisionTile;
+ optional<AlphaImage> glyphAtlasImage;
+ optional<PremultipliedImage> iconAtlasImage;
uint64_t correlationID;
+
+ PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
+ std::unique_ptr<CollisionTile> collisionTile_,
+ optional<AlphaImage> glyphAtlasImage_,
+ optional<PremultipliedImage> iconAtlasImage_,
+ uint64_t correlationID_)
+ : symbolBuckets(std::move(symbolBuckets_)),
+ collisionTile(std::move(collisionTile_)),
+ glyphAtlasImage(std::move(glyphAtlasImage_)),
+ iconAtlasImage(std::move(iconAtlasImage_)),
+ correlationID(correlationID_) {}
};
void onPlacement(PlacementResult);
void onError(std::exception_ptr);
+ 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 };
@@ -99,8 +125,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;
@@ -109,10 +135,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;
+
+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 616a0bba1f..add1ea343c 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,42 @@ 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) {
+ imageMap = std::move(newImageMap);
+ for (const auto& pair : imageMap) {
+ auto it = pendingImageDependencies.find(pair.first);
+ if (it != pendingImageDependencies.end()) {
+ pendingImageDependencies.erase(it);
+ }
+ }
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 +241,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, pendingImageDependencies);
}
}
-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 +273,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 +301,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 +314,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 +353,7 @@ void GeometryTileWorker::redoLayout() {
}
requestNewGlyphs(glyphDependencies);
- requestNewIcons(iconDependencies);
+ requestNewImages(imageDependencies);
parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult {
std::move(buckets),
@@ -359,31 +365,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;
@@ -392,11 +409,6 @@ void GeometryTileWorker::attemptPlacement() {
return;
}
- if (symbolLayout->state == SymbolLayout::Pending) {
- symbolLayout->prepare(glyphPositions, icons);
- symbolLayout->state = SymbolLayout::Placed;
- }
-
if (!symbolLayout->hasSymbolInstances()) {
continue;
}
@@ -410,6 +422,8 @@ void GeometryTileWorker::attemptPlacement() {
parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
std::move(buckets),
std::move(collisionTile),
+ std::move(glyphAtlasImage),
+ std::move(iconAtlasImage),
correlationID
});
}
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 1df1ef43c4..7f80c3b4f7 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);
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,
@@ -71,15 +72,16 @@ private:
uint64_t correlationID = 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 b1a901e565..b940e342d7 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)) {
}
@@ -41,7 +41,7 @@ void RasterTile::setData(std::shared_ptr<const std::string> data,
worker.invoke(&RasterTileWorker::parse, data);
}
-void RasterTile::onParsed(std::unique_ptr<Bucket> result) {
+void RasterTile::onParsed(std::unique_ptr<RasterBucket> result) {
bucket = std::move(result);
loaded = true;
renderable = bucket ? true : false;
@@ -55,10 +55,22 @@ void RasterTile::onError(std::exception_ptr err) {
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 e047430485..28a27b2b37 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);
+ void upload(gl::Context&) override;
+ Bucket* getBucket(const style::Layer::Impl&) const override;
+
+ void setMask(TileMask&&) override;
+
+ void onParsed(std::unique_ptr<RasterBucket> result);
void onError(std::exception_ptr);
private:
@@ -42,7 +47,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 8c1fc2f673..3c8af97b40 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) {
}
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));
} catch (...) {
parent.invoke(&RasterTile::onError, std::current_exception());
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index 0adc151a64..35fc31dae1 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 RenderStyle&,
const RenderedQueryOptions&) {}
void Tile::querySourceFeatures(
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 795fd62140..a1ab6a84b7 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 RenderStyle;
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 RenderStyle&,
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.hpp b/src/mbgl/tile/tile_id.hpp
index 1ce3eea98e..811158e9b9 100644
--- a/src/mbgl/tile/tile_id.hpp
+++ b/src/mbgl/tile/tile_id.hpp
@@ -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 {
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/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/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..fcde01c1a3 100644
--- a/src/mbgl/util/math.hpp
+++ b/src/mbgl/util/math.hpp
@@ -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..77a1416e48 100644
--- a/src/mbgl/util/offscreen_texture.cpp
+++ b/src/mbgl/util/offscreen_texture.cpp
@@ -33,6 +33,7 @@ public:
}
context.activeTexture = 0;
+ context.scissorTest = false;
context.viewport = { 0, 0, size };
}
diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp
index ae96286340..0353f3f9c5 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 {
@@ -15,7 +14,7 @@ enum class OffscreenTextureAttachment {
Depth,
};
-class OffscreenTexture : public View {
+class OffscreenTexture {
public:
OffscreenTexture(gl::Context&,
Size size = { 256, 256 },
@@ -24,7 +23,7 @@ public:
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
index 184c6a8a12..572f46080e 100644
--- a/src/mbgl/util/thread.hpp
+++ b/src/mbgl/util/thread.hpp
@@ -1,63 +1,100 @@
#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 <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 {
+// 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 ThreadContext&, Args&&... args);
- ~Thread();
+ Thread(const std::string& name, Args&&... args) {
+ std::promise<void> running;
- // Invoke object->fn(args...) asynchronously.
- template <typename Fn, class... Args>
- void invoke(Fn fn, Args&&... args) {
- loop->invoke(bind(fn), std::forward<Args>(args)...);
- }
+ thread = std::thread([&] {
+ platform::setCurrentThreadName(name);
+ platform::makeThreadLowPriority();
+
+ util::RunLoop loop_(util::RunLoop::Type::New);
+ loop = &loop_;
- // 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)...);
+ object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...);
+ running.set_value();
+
+ loop->run();
+ loop = nullptr;
+ });
+
+ running.get_future().get();
}
- // Invoke object->fn(args...) asynchronously, but wait for the result.
- template <typename Fn, class... Args>
- auto invokeSync(Fn fn, Args&&... args) {
- assert(!paused);
+ ~Thread() override {
+ MBGL_VERIFY_THREAD(tid);
+
+ 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();
- 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();
+ 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);
@@ -77,6 +114,7 @@ public:
pausing.get();
}
+ // Resumes the `Object` thread previously paused by `pause()`.
void resume() {
MBGL_VERIFY_THREAD(tid);
@@ -91,83 +129,35 @@ public:
private:
MBGL_STORE_THREAD(tid);
- Thread(const Thread&) = delete;
- Thread(Thread&&) = delete;
- Thread& operator=(const Thread&) = delete;
- Thread& operator=(Thread&&) = delete;
+ void schedule(std::weak_ptr<Mailbox> mailbox) override {
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ queue.push(mailbox);
+ }
- template <typename Fn>
- auto bind(Fn fn) {
- return [fn, this] (auto &&... args) {
- return (object->*fn)(std::forward<decltype(args)>(args)...);
- };
+ loop->invoke([this] { receive(); });
}
- template <typename P, std::size_t... I>
- void run(P&& params, std::index_sequence<I...>);
+ void receive() {
+ std::unique_lock<std::mutex> lock(mutex);
- std::promise<void> running;
- std::promise<void> joinable;
+ auto mailbox = queue.front();
+ queue.pop();
+ lock.unlock();
- std::unique_ptr<std::promise<void>> paused;
- std::unique_ptr<std::promise<void>> resumed;
+ Mailbox::maybeReceive(mailbox);
+ }
+ std::mutex mutex;
+ std::queue<std::weak_ptr<Mailbox>> queue;
std::thread thread;
+ std::unique_ptr<Actor<Object>> object;
- 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();
- }
+ std::unique_ptr<std::promise<void>> paused;
+ std::unique_ptr<std::promise<void>> resumed;
- loop->stop();
- joinable.set_value();
- thread.join();
-}
+ util::RunLoop* loop = nullptr;
+};
} // 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/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..4c7fc3666d 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,58 @@ 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, 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..52b0de295b 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,58 @@ 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, 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..3c60a5e2f2
--- /dev/null
+++ b/test/api/zoom_history.cpp
@@ -0,0 +1,65 @@
+#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);
+}
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/prefetch/empty.json b/test/fixtures/map/prefetch/empty.json
new file mode 100644
index 0000000000..61a8fadcdb
--- /dev/null
+++ b/test/fixtures/map/prefetch/empty.json
@@ -0,0 +1,5 @@
+{
+ "version": 8,
+ "sources": {},
+ "layers": []
+}
diff --git a/test/fixtures/map/prefetch/expected.png b/test/fixtures/map/prefetch/expected.png
new file mode 100644
index 0000000000..e1111b37f7
--- /dev/null
+++ b/test/fixtures/map/prefetch/expected.png
Binary files differ
diff --git a/test/fixtures/map/prefetch/style.json b/test/fixtures/map/prefetch/style.json
new file mode 100644
index 0000000000..b4e163888c
--- /dev/null
+++ b/test/fixtures/map/prefetch/style.json
@@ -0,0 +1,24 @@
+{
+ "version": 8,
+ "name": "Test",
+ "sources": {
+ "raster": {
+ "type": "raster",
+ "tiles": [ "{z}" ],
+ "tileSize": 256,
+ "maxzoom": 20,
+ "minzoom": 0
+ }
+ },
+ "layers": [{
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "blue"
+ }
+ }, {
+ "id": "raster",
+ "type": "raster",
+ "source": "raster"
+ }]
+}
diff --git a/test/fixtures/map/prefetch/tile_green.png b/test/fixtures/map/prefetch/tile_green.png
new file mode 100644
index 0000000000..553cd10cd1
--- /dev/null
+++ b/test/fixtures/map/prefetch/tile_green.png
Binary files differ
diff --git a/test/fixtures/map/prefetch/tile_red.png b/test/fixtures/map/prefetch/tile_red.png
new file mode 100644
index 0000000000..5fa561fb92
--- /dev/null
+++ b/test/fixtures/map/prefetch/tile_red.png
Binary files differ
diff --git a/test/fixtures/offline_database/v5.db b/test/fixtures/offline_database/v5.db
new file mode 100644
index 0000000000..9bba351208
--- /dev/null
+++ b/test/fixtures/offline_database/v5.db
Binary files differ
diff --git a/test/fixtures/offline_database/v999.db b/test/fixtures/offline_database/v999.db
new file mode 100644
index 0000000000..97c36cce36
--- /dev/null
+++ b/test/fixtures/offline_database/v999.db
Binary files differ
diff --git a/test/fixtures/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/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..98f8402706 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);
+ MapTest<> test;
- map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.setLatLngZoom({ 1, 1 }, 0);
+ auto latLng1 = test.map.getLatLng();
- map.setLatLngZoom({ 1, 1 }, 0);
- auto latLng1 = 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;
+ MapTest<DefaultFileSource> test { ":memory:", "test/fixtures/api/assets" };
- test.backend.getContext().disableVAOExtension = true;
+ BackendScope scope { *test.frontend.getBackend() };
+ test.frontend.getBackend()->getContext().disableVAOExtension = true;
- DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
-
- 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,42 @@ 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();
}
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..06bf1e7750 100644
--- a/test/renderer/backend_scope.test.cpp
+++ b/test/renderer/backend_scope.test.cpp
@@ -1,14 +1,16 @@
#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 {
+ }
void activate() override {
if (activateFunction) activateFunction();
@@ -22,8 +24,6 @@ public:
if (updateAssumedStateFunction) updateAssumedStateFunction();
}
- void invalidate() override {}
-
gl::ProcAddress initializeExtension(const char* ext) override {
if (initializeExtensionFunction) {
return initializeExtensionFunction(ext);
@@ -45,7 +45,7 @@ TEST(BackendScope, SingleScope) {
bool activated;
bool deactivated;
- StubBackend backend;
+ StubRendererBackend backend;
backend.activateFunction = [&] { activated = true; };
backend.deactivateFunction = [&] { deactivated = true; };
@@ -63,7 +63,7 @@ TEST(BackendScope, NestedScopes) {
int activated = 0;
int deactivated = 0;
- StubBackend backend;
+ StubRendererBackend backend;
backend.activateFunction = [&] { activated++; };
backend.deactivateFunction = [&] { deactivated++; };
@@ -88,11 +88,11 @@ 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; };
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..5e6da5c005
--- /dev/null
+++ b/test/renderer/image_manager.test.cpp
@@ -0,0 +1,147 @@
+#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) final {
+ if (imagesAvailable) imagesAvailable(images);
+ }
+
+ std::function<void (ImageMap)> imagesAvailable;
+};
+
+TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) {
+ ImageManager imageManager;
+ StubImageRequestor requestor;
+ bool notified = false;
+
+ requestor.imagesAvailable = [&] (ImageMap) {
+ notified = true;
+ };
+
+ imageManager.getImages(requestor, {"one"});
+ ASSERT_FALSE(notified);
+
+ imageManager.setLoaded(true);
+ ASSERT_TRUE(notified);
+}
+
+TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) {
+ ImageManager imageManager;
+ StubImageRequestor requestor;
+ bool notified = false;
+
+ requestor.imagesAvailable = [&] (ImageMap) {
+ notified = true;
+ };
+
+ imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2));
+ imageManager.getImages(requestor, {"one"});
+
+ 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..b8aebae7e7 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,15 +536,34 @@ 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();
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_database.test.cpp b/test/storage/offline_database.test.cpp
index 872310e46f..d99c1f946f 100644
--- a/test/storage/offline_database.test.cpp
+++ b/test/storage/offline_database.test.cpp
@@ -634,24 +634,35 @@ static int databaseSyncMode(const std::string& path) {
return stmt.get<int>(0);
}
+static std::vector<std::string> databaseTableColumns(const std::string& path, const std::string& name) {
+ mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly);
+ const auto sql = std::string("pragma table_info(") + name + ")";
+ mapbox::sqlite::Statement stmt = db.prepare(sql.c_str());
+ std::vector<std::string> columns;
+ while (stmt.run()) {
+ columns.push_back(stmt.get<std::string>(1));
+ }
+ return columns;
+}
+
TEST(OfflineDatabase, MigrateFromV2Schema) {
using namespace mbgl;
// v2.db is a v2 database containing a single offline region with a small number of resources.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v2.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v2.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
- EXPECT_LT(databasePageCount("test/fixtures/offline_database/v5.db"),
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+ EXPECT_LT(databasePageCount("test/fixtures/offline_database/migrated.db"),
databasePageCount("test/fixtures/offline_database/v2.db"));
}
@@ -660,18 +671,18 @@ TEST(OfflineDatabase, MigrateFromV3Schema) {
// v3.db is a v3 database, migrated from v2.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v3.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v3.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
}
TEST(OfflineDatabase, MigrateFromV4Schema) {
@@ -679,22 +690,74 @@ TEST(OfflineDatabase, MigrateFromV4Schema) {
// v4.db is a v4 database, migrated from v2 & v3. This database used `journal_mode = WAL` and `synchronous = NORMAL`.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v4.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v4.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
// Journal mode should be DELETE after migration to v5.
- EXPECT_EQ("delete", databaseJournalMode("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ("delete", databaseJournalMode("test/fixtures/offline_database/migrated.db"));
// Synchronous setting should be FULL (2) after migration to v5.
- EXPECT_EQ(2, databaseSyncMode("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(2, databaseSyncMode("test/fixtures/offline_database/migrated.db"));
+}
+
+
+TEST(OfflineDatabase, MigrateFromV5Schema) {
+ using namespace mbgl;
+
+ // v5.db is a v5 database, migrated from v2, v3 & v4.
+
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v5.db"));
+
+ {
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
+ auto regions = db.listRegions();
+ for (auto& region : regions) {
+ db.deleteRegion(std::move(region));
+ }
+ }
+
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+
+ EXPECT_EQ((std::vector<std::string>{ "id", "url_template", "pixel_ratio", "z", "x", "y",
+ "expires", "modified", "etag", "data", "compressed",
+ "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "tiles"));
+ EXPECT_EQ((std::vector<std::string>{ "id", "url", "kind", "expires", "modified", "etag", "data",
+ "compressed", "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "resources"));
+}
+
+TEST(OfflineDatabase, DowngradeSchema) {
+ using namespace mbgl;
+
+ // v999.db is a v999 database, it should be deleted
+ // and recreated with the current schema.
+
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v999.db"));
+
+ {
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
+ }
+
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+
+ EXPECT_EQ((std::vector<std::string>{ "id", "url_template", "pixel_ratio", "z", "x", "y",
+ "expires", "modified", "etag", "data", "compressed",
+ "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "tiles"));
+ EXPECT_EQ((std::vector<std::string>{ "id", "url", "kind", "expires", "modified", "etag", "data",
+ "compressed", "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "resources"));
}
diff --git a/test/storage/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..c4c1a7ac15 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);
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);
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 4d71c5b0b4..c58b980ecd 100644
--- a/test/tile/annotation_tile.test.cpp
+++ b/test/tile/annotation_tile.test.cpp
@@ -4,14 +4,18 @@
#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/render_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 +27,13 @@ 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 };
+ RenderStyle renderStyle { threadPool, fileSource };
+ ImageManager imageManager;
+ GlyphManager glyphManager { fileSource };
TileParameters tileParameters {
1.0,
@@ -34,7 +43,9 @@ public:
fileSource,
MapMode::Continuous,
annotationManager,
- style
+ imageManager,
+ glyphManager,
+ 0
};
};
@@ -44,15 +55,14 @@ 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),
- 0
+ std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
+ std::make_unique<FeatureIndex>(),
+ std::move(data),
+ 0
});
auto collisionTile = std::make_unique<CollisionTile>(PlacementConfig());
@@ -63,17 +73,19 @@ 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
+ {},
+ 0
});
// Simulate a second layout with empty data.
tile.onLayout(GeometryTile::LayoutResult {
- {},
- std::make_unique<FeatureIndex>(),
- std::make_unique<AnnotationTileData>(),
- 0
+ std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
+ std::make_unique<FeatureIndex>(),
+ std::make_unique<AnnotationTileData>(),
+ 0
});
std::unordered_map<std::string, std::vector<Feature>> result;
@@ -81,7 +93,7 @@ TEST(AnnotationTile, Issue8289) {
TransformState transformState;
RenderedQueryOptions options;
- tile.queryRenderedFeatures(result, queryGeometry, transformState, options);
+ tile.queryRenderedFeatures(result, queryGeometry, transformState, test.renderStyle, options);
EXPECT_TRUE(result.empty());
}
diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp
index a0383f06c9..31fb8c1fd0 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({});
diff --git a/test/tile/raster_tile.test.cpp b/test/tile/raster_tile.test.cpp
index f841a82e68..e96e09bacf 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{}));
+ tile.onParsed(std::make_unique<RasterBucket>(PremultipliedImage{}));
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 37bfe8512d..45eab21576 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
};
};
@@ -68,7 +74,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.
@@ -78,18 +84,20 @@ 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/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);
-}