summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanswick <dan.swick@gmail.com>2019-09-17 14:09:45 -0700
committerdanswick <dan.swick@gmail.com>2019-09-17 14:09:45 -0700
commit47ea84ad1a5fed3431d026c5765c17c507d4623e (patch)
tree891a60152525bffedc916b878ce248c515c80e4f
parente6c6a73cb99d5942ba0efd5b9bb86ad5ad77dcfd (diff)
parentfc7d2938dd7352055ed0562db5235c97e5a01745 (diff)
downloadqtlocation-mapboxgl-47ea84ad1a5fed3431d026c5765c17c507d4623e.tar.gz
Merge branch 'master' into android-docs-automation
-rw-r--r--.clang-format23
-rw-r--r--.clang-tidy3
-rw-r--r--.cmake-format91
-rw-r--r--.github/stale.yml4
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules24
-rw-r--r--CMakeLists.txt9
-rw-r--r--Makefile116
-rw-r--r--appveyor.yml66
-rw-r--r--benchmark/fixtures/api/cache.dbbin1298432 -> 1298432 bytes
-rw-r--r--benchmark/include/mbgl/benchmark.hpp4
-rw-r--r--bin/offline.cpp2
-rw-r--r--circle.yml567
-rw-r--r--cmake/benchmark.cmake2
-rw-r--r--cmake/core.cmake30
-rw-r--r--cmake/expression-test.cmake27
-rw-r--r--cmake/filesource.cmake2
-rw-r--r--cmake/glfw.cmake5
-rw-r--r--cmake/offline.cmake6
-rw-r--r--cmake/render-test.cmake11
-rw-r--r--cmake/render.cmake2
-rw-r--r--cmake/test.cmake10
-rw-r--r--cmake/vendor.cmake10
-rw-r--r--expression-test/expression_test_logger.cpp177
-rw-r--r--expression-test/expression_test_logger.hpp14
-rw-r--r--expression-test/expression_test_parser.cpp536
-rw-r--r--expression-test/expression_test_parser.hpp94
-rw-r--r--expression-test/expression_test_runner.cpp303
-rw-r--r--expression-test/expression_test_runner.hpp32
-rw-r--r--expression-test/filesystem.hpp9
-rw-r--r--expression-test/main.cpp75
-rw-r--r--include/mbgl/annotation/annotation.hpp1
-rw-r--r--include/mbgl/map/map.hpp2
-rw-r--r--include/mbgl/map/map_observer.hpp8
-rw-r--r--include/mbgl/renderer/renderer.hpp1
-rw-r--r--include/mbgl/renderer/renderer_observer.hpp4
-rw-r--r--include/mbgl/storage/offline.hpp5
-rw-r--r--include/mbgl/storage/resource.hpp18
-rw-r--r--include/mbgl/style/expression/dsl.hpp29
-rw-r--r--include/mbgl/style/expression/expression.hpp6
-rw-r--r--include/mbgl/style/layer.hpp11
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp4
-rw-r--r--include/mbgl/style/light.hpp4
-rw-r--r--include/mbgl/style/light.hpp.ejs4
-rw-r--r--include/mbgl/style/source.hpp8
-rw-r--r--include/mbgl/style/sources/custom_geometry_source.hpp6
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp20
-rw-r--r--include/mbgl/style/sources/image_source.hpp7
-rw-r--r--include/mbgl/style/sources/raster_source.hpp5
-rw-r--r--include/mbgl/style/sources/vector_source.hpp12
-rw-r--r--include/mbgl/style/types.hpp5
-rw-r--r--include/mbgl/util/bitmask_operations.hpp33
-rw-r--r--include/mbgl/util/color.hpp2
-rw-r--r--include/mbgl/util/default_styles.hpp (renamed from platform/default/include/mbgl/util/default_styles.hpp)13
-rw-r--r--include/mbgl/util/peer.hpp34
-rw-r--r--include/mbgl/util/util.hpp23
m---------mapbox-gl-js0
-rw-r--r--misc/buck/mapbox-gl-native/BUCK6
-rw-r--r--next/CMakeLists.txt941
-rw-r--r--next/README.md42
-rw-r--r--next/benchmark/CMakeLists.txt41
-rw-r--r--next/bin/CMakeLists.txt26
-rw-r--r--next/expression-test/CMakeLists.txt36
-rw-r--r--next/platform/android/android.cmake306
-rw-r--r--next/platform/glfw/CMakeLists.txt38
-rw-r--r--next/platform/ios/ios.cmake70
-rw-r--r--next/platform/linux/linux.cmake126
-rw-r--r--next/platform/macos/macos.cmake187
-rw-r--r--next/platform/node/CMakeLists.txt52
-rw-r--r--next/platform/qt/qt.cmake190
-rw-r--r--next/render-test/CMakeLists.txt50
-rw-r--r--next/test/CMakeLists.txt167
l---------next/vendor1
-rw-r--r--package.json11
-rw-r--r--platform/android/.gitignore2
-rw-r--r--platform/android/CHANGELOG.md105
-rw-r--r--platform/android/LICENSE.md6
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java97
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java80
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java49
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java34
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java191
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java8
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java106
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java10
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java79
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java37
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java187
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java32
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java27
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/FontUtils.java81
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java42
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java35
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt101
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt103
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/http/HttpRequestUrlTest.kt4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt253
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt60
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapInstrumentationTest.kt41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt38
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt64
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/offline/OfflineDownloadTest.kt31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseTest.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java296
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java290
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java292
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java307
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java200
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/FontUtilsTest.java41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java26
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_backstack_fragment.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml5
-rw-r--r--platform/android/build.gradle2
-rw-r--r--platform/android/config.cmake4
-rw-r--r--platform/android/core-files.json2
-rw-r--r--platform/android/gradle/dependencies.gradle4
-rw-r--r--platform/android/gradle/jacoco-report.gradle2
-rw-r--r--platform/android/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--platform/android/scripts/exclude-activity-gen.json5
-rwxr-xr-xplatform/android/scripts/generate-style-code.js11
-rw-r--r--platform/android/src/android_renderer_frontend.cpp4
-rw-r--r--platform/android/src/java_types.cpp8
-rw-r--r--platform/android/src/java_types.hpp6
-rwxr-xr-xplatform/android/src/jni.cpp157
-rw-r--r--platform/android/src/jni.hpp5
-rwxr-xr-xplatform/android/src/jni_native.cpp164
-rw-r--r--platform/android/src/jni_native.hpp13
-rw-r--r--platform/android/src/main.cpp2
-rw-r--r--platform/android/src/map/camera_position.cpp22
-rw-r--r--platform/android/src/map/camera_position.hpp4
-rw-r--r--platform/android/src/map_renderer.cpp6
-rw-r--r--platform/android/src/map_renderer.hpp2
-rwxr-xr-xplatform/android/src/native_map_view.cpp67
-rwxr-xr-xplatform/android/src/native_map_view.hpp15
-rw-r--r--platform/android/src/run_loop.cpp16
-rw-r--r--platform/android/src/run_loop_impl.hpp1
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.cpp8
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.hpp1
-rw-r--r--platform/android/src/style/android_conversion.hpp18
-rw-r--r--platform/android/src/style/layers/symbol_layer.cpp6
-rw-r--r--platform/android/src/style/layers/symbol_layer.hpp2
-rw-r--r--platform/android/src/style/sources/custom_geometry_source.cpp14
-rw-r--r--platform/android/src/style/sources/source.cpp2
-rw-r--r--platform/android/src/style/value.cpp6
-rw-r--r--platform/android/src/style/value.hpp1
-rw-r--r--platform/android/src/test/runtime.cpp1
m---------platform/android/vendor/mapbox-gestures-android0
m---------platform/android/vendor/mapbox-java0
-rwxr-xr-xplatform/darwin/scripts/generate-style-code.js88
-rw-r--r--platform/darwin/scripts/style-spec-cocoa-conventions-v8.json3
-rw-r--r--platform/darwin/src/MGLAccountManager.m2
-rw-r--r--platform/darwin/src/MGLAccountManager_Private.h3
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.mm3
-rw-r--r--platform/darwin/src/MGLForegroundStyleLayer.mm16
-rw-r--r--platform/darwin/src/MGLImageSource.mm19
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h33
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm107
-rw-r--r--platform/darwin/src/MGLOfflineStorage.h10
-rw-r--r--platform/darwin/src/MGLRasterTileSource.mm2
-rw-r--r--platform/darwin/src/MGLRasterTileSource_Private.h2
-rw-r--r--platform/darwin/src/MGLRendererConfiguration.h3
-rw-r--r--platform/darwin/src/MGLRendererConfiguration.mm4
-rw-r--r--platform/darwin/src/MGLSDKMetricsManager.m15
-rw-r--r--platform/darwin/src/MGLShapeSource.mm16
-rw-r--r--platform/darwin/src/MGLSource.h3
-rw-r--r--platform/darwin/src/MGLSource.mm15
-rw-r--r--platform/darwin/src/MGLSource_Private.h15
-rw-r--r--platform/darwin/src/MGLStyle.mm21
-rw-r--r--platform/darwin/src/MGLStyleLayer.h.ejs30
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm22
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm.ejs20
-rw-r--r--platform/darwin/src/MGLStyleLayer_Private.h1
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h91
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm40
-rw-r--r--platform/darwin/src/MGLTypes.h4
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.m25
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.mm34
-rw-r--r--platform/darwin/src/MGLVectorTileSource.mm4
-rw-r--r--platform/darwin/src/http_file_source.mm27
-rw-r--r--platform/darwin/src/string_nsstring.mm4
-rw-r--r--platform/darwin/test/MGLClockDirectionFormatterTests.m5
-rw-r--r--platform/darwin/test/MGLCompassDirectionFormatterTests.m5
-rw-r--r--platform/darwin/test/MGLCoordinateFormatterTests.m5
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift5
-rw-r--r--platform/darwin/test/MGLDocumentationGuideTests.swift6
-rw-r--r--platform/darwin/test/MGLResourceTests.mm23
-rw-r--r--platform/darwin/test/MGLSDKTestHelpers.swift1
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.mm.ejs2
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm47
-rw-r--r--platform/darwin/test/MGLTileSetTests.mm19
-rw-r--r--platform/default/include/mbgl/gfx/headless_frontend.hpp2
-rw-r--r--platform/default/include/mbgl/map/map_snapshotter.hpp1
-rw-r--r--platform/default/include/mbgl/storage/offline_database.hpp5
-rw-r--r--platform/default/include/mbgl/storage/offline_download.hpp4
-rw-r--r--platform/default/src/mbgl/gfx/headless_frontend.cpp6
-rw-r--r--platform/default/src/mbgl/map/map_snapshotter.cpp7
-rw-r--r--platform/default/src/mbgl/storage/http_file_source.cpp168
-rw-r--r--platform/default/src/mbgl/storage/offline_database.cpp33
-rw-r--r--platform/default/src/mbgl/storage/offline_download.cpp62
-rw-r--r--platform/default/src/mbgl/storage/sqlite3.cpp1
-rw-r--r--platform/default/src/mbgl/util/format_number.cpp12
-rw-r--r--platform/ios/CHANGELOG.md51
-rw-r--r--platform/ios/INSTALL.md22
-rw-r--r--platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm (renamed from platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m)273
-rw-r--r--platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm109
-rw-r--r--platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionTests.mm (renamed from platform/ios/Integration Tests/MGLCameraTransitionTests.mm)24
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.h3
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.m79
-rw-r--r--platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m366
-rw-r--r--platform/ios/Integration Tests/MGLSourceTests.swift45
-rw-r--r--platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m51
-rw-r--r--platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m16
-rw-r--r--platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift23
-rw-r--r--platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m122
-rw-r--r--platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK-stripped.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK.podspec2
-rw-r--r--platform/ios/Mapbox.playground/Contents.swift7
-rw-r--r--platform/ios/Mapbox.playground/contents.xcplayground2
-rw-r--r--platform/ios/app/MBXAnnotationView.h2
-rw-r--r--platform/ios/app/MBXAppDelegate.m3
-rw-r--r--platform/ios/app/MBXCustomCalloutView.h3
-rw-r--r--platform/ios/app/MBXCustomLocationViewController.m4
-rw-r--r--platform/ios/app/MBXEmbeddedMapViewController.m3
-rw-r--r--platform/ios/app/MBXOfflinePacksTableViewController.m3
-rw-r--r--platform/ios/app/MBXOrnamentsViewController.m4
-rw-r--r--platform/ios/app/MBXSnapshotsViewController.m4
-rw-r--r--platform/ios/app/MBXState.h5
-rw-r--r--platform/ios/app/MBXState.m9
-rw-r--r--platform/ios/app/MBXStateManager.m1
-rw-r--r--platform/ios/app/MBXUserLocationAnnotationView.h2
-rw-r--r--platform/ios/app/MBXViewController.m59
-rw-r--r--platform/ios/app/Main.storyboard14
-rw-r--r--platform/ios/benchmark/MBXBenchAppDelegate.m1
-rw-r--r--platform/ios/benchmark/MBXBenchViewController.mm3
-rw-r--r--platform/ios/config.cmake2
-rw-r--r--platform/ios/core-files.json3
-rw-r--r--platform/ios/docs/guides/For Style Authors.md1
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj80
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme2
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme26
-rwxr-xr-xplatform/ios/scripts/deploy-packages.sh6
-rwxr-xr-xplatform/ios/scripts/install-packaging-dependencies.sh9
-rwxr-xr-xplatform/ios/scripts/publish.sh44
-rw-r--r--platform/ios/sdk-files.json2
-rw-r--r--platform/ios/src/MGLCompassButton.mm11
-rw-r--r--platform/ios/src/MGLCompassButton_Private.h2
-rw-r--r--platform/ios/src/MGLMapView+Impl.h2
-rw-r--r--platform/ios/src/MGLMapView+Impl.mm4
-rw-r--r--platform/ios/src/MGLMapView.h41
-rw-r--r--platform/ios/src/MGLMapView.mm339
-rw-r--r--platform/ios/src/MGLMapView_Private.h2
-rw-r--r--platform/ios/src/MGLMapboxEvents.h3
-rw-r--r--platform/ios/src/MGLMapboxEvents.m66
-rw-r--r--platform/ios/src/MGLScaleBar.mm20
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.h2
-rw-r--r--platform/ios/test/MGLMapAccessibilityElementTests.m5
-rw-r--r--platform/ios/test/MGLMapViewDirectionTests.mm7
-rw-r--r--platform/ios/test/MGLMapViewPitchTests.m39
-rw-r--r--platform/ios/test/MGLMapViewZoomTests.mm (renamed from platform/ios/test/MGLMapViewZoomTests.m)68
-rw-r--r--platform/ios/test/MGLMockGestureRecognizers.h10
-rw-r--r--platform/ios/test/MGLMockGestureRecognizers.m11
-rw-r--r--platform/ios/vendor/mapbox-accounts-ios/libmbxaccounts.abin96784 -> 97472 bytes
-rw-r--r--platform/linux/README.md4
-rw-r--r--platform/linux/config.cmake10
-rw-r--r--platform/macos/CHANGELOG.md12
-rw-r--r--platform/macos/docs/guides/For Style Authors.md1
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj16
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme30
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme26
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme25
-rw-r--r--platform/macos/sdk-files.json2
-rw-r--r--platform/macos/src/MGLAnnotationImage_Private.h2
-rw-r--r--platform/macos/src/MGLMapView+Impl.h2
-rw-r--r--platform/macos/src/MGLMapView+Impl.mm4
-rw-r--r--platform/macos/src/MGLMapView.h4
-rw-r--r--platform/macos/src/MGLMapView.mm2
-rw-r--r--platform/node/CHANGELOG.md17
-rw-r--r--platform/node/DEVELOPING.md2
-rw-r--r--platform/node/src/node_expression.cpp5
-rw-r--r--platform/node/test/ignores.json25
-rw-r--r--platform/qt/config.cmake75
-rw-r--r--platform/qt/qnx.cmake77
-rw-r--r--platform/qt/qt.cmake187
-rw-r--r--platform/qt/src/local_glyph_rasterizer.cpp25
-rw-r--r--platform/qt/src/qmapboxgl_map_observer.cpp4
-rw-r--r--platform/qt/src/qmapboxgl_map_observer.hpp2
-rw-r--r--platform/qt/src/qmapboxgl_map_renderer.cpp2
-rw-r--r--platform/qt/src/qmapboxgl_renderer_observer.hpp4
-rw-r--r--platform/qt/test/qmapboxgl.test.cpp15
-rw-r--r--platform/qt/test/qmapboxgl.test.hpp5
-rw-r--r--render-test/allocation_index.cpp101
-rw-r--r--render-test/allocation_index.hpp53
-rw-r--r--render-test/expected/debug/collision-lines-overscaled/expected.pngbin0 -> 7774 bytes
-rw-r--r--render-test/expected/debug/collision-lines-pitched/expected.pngbin0 -> 165500 bytes
-rw-r--r--render-test/expected/debug/collision-lines/expected.pngbin0 -> 196454 bytes
-rw-r--r--render-test/expected/debug/collision-pitched-wrapped/expected.pngbin0 -> 69725 bytes
-rw-r--r--render-test/expected/symbol-visibility/visible/expected.pngbin0 -> 14729 bytes
-rw-r--r--render-test/expected/text-pitch-alignment/auto-text-rotation-alignment-map/expected.pngbin0 -> 26157 bytes
-rw-r--r--render-test/expected/text-pitch-alignment/map-text-rotation-alignment-map/expected.pngbin0 -> 26157 bytes
-rw-r--r--render-test/expected/text-pitch-alignment/viewport-text-rotation-alignment-map/expected.pngbin0 -> 26866 bytes
-rw-r--r--render-test/expected/text-variable-anchor/pitched-rotated-debug/expected.pngbin0 -> 43135 bytes
-rw-r--r--render-test/expected/text-variable-anchor/rotated-offset/expected.pngbin0 -> 35553 bytes
-rw-r--r--render-test/linux-ignores.json11
-rw-r--r--render-test/mac-ignores.json5
-rw-r--r--render-test/main.cpp38
-rw-r--r--render-test/metadata.hpp35
-rw-r--r--render-test/parser.cpp145
-rw-r--r--render-test/parser.hpp15
-rw-r--r--render-test/runner.cpp129
-rw-r--r--render-test/runner.hpp7
-rwxr-xr-xscripts/check-ci-job-skippability.js121
-rwxr-xr-xscripts/check-cxx11abi.datbin8780 -> 0 bytes
-rwxr-xr-xscripts/check-cxx11abi.sh21
-rw-r--r--scripts/ci/Dockerfile110
-rw-r--r--scripts/ci/etc/apt/sources.list10
-rwxr-xr-xscripts/environment.js2
-rwxr-xr-xscripts/generate-file-lists.js23
-rw-r--r--src/core-files.json3
-rw-r--r--src/mbgl/annotation/annotation_source.hpp7
-rw-r--r--src/mbgl/geometry/dem_data.cpp70
-rw-r--r--src/mbgl/geometry/dem_data.hpp28
-rw-r--r--src/mbgl/layout/symbol_feature.hpp1
-rw-r--r--src/mbgl/layout/symbol_instance.cpp65
-rw-r--r--src/mbgl/layout/symbol_instance.hpp35
-rw-r--r--src/mbgl/layout/symbol_layout.cpp282
-rw-r--r--src/mbgl/layout/symbol_layout.hpp20
-rw-r--r--src/mbgl/layout/symbol_projection.cpp6
-rw-r--r--src/mbgl/map/map.cpp2
-rw-r--r--src/mbgl/map/map_impl.cpp4
-rw-r--r--src/mbgl/map/map_impl.hpp2
-rw-r--r--src/mbgl/map/transform.cpp56
-rw-r--r--src/mbgl/map/transform.hpp12
-rw-r--r--src/mbgl/map/transform_state.cpp17
-rw-r--r--src/mbgl/map/transform_state.hpp5
-rw-r--r--src/mbgl/programs/gl/hillshade.cpp4
-rw-r--r--src/mbgl/programs/gl/hillshade_prepare.cpp6
-rw-r--r--src/mbgl/programs/gl/line.cpp4
-rw-r--r--src/mbgl/programs/gl/line_gradient.cpp4
-rw-r--r--src/mbgl/programs/gl/line_pattern.cpp4
-rw-r--r--src/mbgl/programs/gl/line_sdf.cpp4
-rw-r--r--src/mbgl/programs/gl/raster.cpp4
-rw-r--r--src/mbgl/programs/gl/shader_source.cpp895
-rw-r--r--src/mbgl/programs/gl/symbol_icon.cpp4
-rw-r--r--src/mbgl/programs/gl/symbol_sdf_icon.cpp4
-rw-r--r--src/mbgl/programs/gl/symbol_sdf_text.cpp4
-rw-r--r--src/mbgl/programs/hillshade_prepare_program.hpp4
-rw-r--r--src/mbgl/programs/program_parameters.cpp22
-rw-r--r--src/mbgl/programs/program_parameters.hpp4
-rw-r--r--src/mbgl/programs/symbol_program.cpp2
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.cpp42
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp113
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp66
-rw-r--r--src/mbgl/renderer/image_manager.cpp1
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp25
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp5
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp5
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp5
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp18
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.cpp5
-rw-r--r--src/mbgl/renderer/layers/render_hillshade_layer.cpp6
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp5
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.cpp5
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp181
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.hpp6
-rw-r--r--src/mbgl/renderer/render_layer.cpp2
-rw-r--r--src/mbgl/renderer/render_orchestrator.cpp29
-rw-r--r--src/mbgl/renderer/render_pass.hpp15
-rw-r--r--src/mbgl/renderer/render_static_data.cpp6
-rw-r--r--src/mbgl/renderer/render_static_data.hpp2
-rw-r--r--src/mbgl/renderer/render_tree.hpp1
-rw-r--r--src/mbgl/renderer/renderer.cpp2
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp9
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp2
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp6
-rw-r--r--src/mbgl/style/conversion/constant.cpp1
-rw-r--r--src/mbgl/style/conversion/filter.cpp7
-rw-r--r--src/mbgl/style/conversion/function.cpp2
-rw-r--r--src/mbgl/style/conversion/geojson_options.cpp69
-rw-r--r--src/mbgl/style/conversion/property_value.cpp1
-rw-r--r--src/mbgl/style/conversion/source.cpp21
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp13
-rw-r--r--src/mbgl/style/expression/dsl.cpp23
-rw-r--r--src/mbgl/style/expression/expression.cpp8
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp3
-rw-r--r--src/mbgl/style/expression/value.cpp3
-rw-r--r--src/mbgl/style/layers/background_layer.cpp15
-rw-r--r--src/mbgl/style/layers/circle_layer.cpp47
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer.cpp35
-rw-r--r--src/mbgl/style/layers/fill_layer.cpp31
-rw-r--r--src/mbgl/style/layers/fill_layer_impl.cpp1
-rw-r--r--src/mbgl/style/layers/heatmap_layer.cpp23
-rw-r--r--src/mbgl/style/layers/hillshade_layer.cpp27
-rw-r--r--src/mbgl/style/layers/layer.cpp.ejs9
-rw-r--r--src/mbgl/style/layers/line_layer.cpp55
-rw-r--r--src/mbgl/style/layers/raster_layer.cpp35
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp169
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.hpp15
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp8
-rw-r--r--src/mbgl/style/light.cpp120
-rw-r--r--src/mbgl/style/light.cpp.ejs78
-rw-r--r--src/mbgl/style/sources/custom_geometry_source.cpp2
-rw-r--r--src/mbgl/style/sources/geojson_source.cpp2
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.cpp69
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.hpp2
-rw-r--r--src/mbgl/style/sources/raster_source.cpp1
-rw-r--r--src/mbgl/style/sources/vector_source.cpp16
-rw-r--r--src/mbgl/style/style_impl.cpp5
-rw-r--r--src/mbgl/style/types.cpp5
-rw-r--r--src/mbgl/text/collision_feature.cpp6
-rw-r--r--src/mbgl/text/collision_feature.hpp59
-rw-r--r--src/mbgl/text/collision_index.cpp118
-rw-r--r--src/mbgl/text/collision_index.hpp22
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.cpp19
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.hpp9
-rw-r--r--src/mbgl/text/glyph.hpp23
-rw-r--r--src/mbgl/text/glyph_manager.cpp7
-rw-r--r--src/mbgl/text/placement.cpp580
-rw-r--r--src/mbgl/text/placement.hpp25
-rw-r--r--src/mbgl/text/quads.cpp86
-rw-r--r--src/mbgl/text/quads.hpp7
-rw-r--r--src/mbgl/text/shaping.cpp83
-rw-r--r--src/mbgl/text/shaping.hpp12
-rw-r--r--src/mbgl/text/tagged_string.cpp8
-rw-r--r--src/mbgl/text/tagged_string.hpp2
-rw-r--r--src/mbgl/tile/geojson_tile.cpp4
-rw-r--r--src/mbgl/tile/geojson_tile.hpp2
-rw-r--r--src/mbgl/tile/geometry_tile.cpp4
-rw-r--r--src/mbgl/tile/geometry_tile.hpp2
-rw-r--r--src/mbgl/tile/geometry_tile_data.cpp5
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp3
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp2
-rw-r--r--src/mbgl/util/chrono.cpp9
-rw-r--r--src/mbgl/util/color.cpp7
-rw-r--r--src/mbgl/util/i18n.cpp14
-rw-r--r--src/mbgl/util/i18n.hpp2
-rw-r--r--src/mbgl/util/tile_coordinate.hpp5
-rw-r--r--src/mbgl/util/tile_cover.cpp12
-rw-r--r--src/mbgl/util/tile_cover.hpp10
-rw-r--r--test/fixtures/local_glyphs/mixed.json4
-rw-r--r--test/fixtures/local_glyphs/no_local/expected.pngbin6963 -> 8855 bytes
-rw-r--r--test/fixtures/local_glyphs/no_local_with_content_insets/expected.pngbin6784 -> 8823 bytes
-rw-r--r--test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.pngbin6026 -> 7537 bytes
-rw-r--r--test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.pngbin0 -> 27980 bytes
-rw-r--r--test/fixtures/local_glyphs/ping_fang/expected.pngbin18019 -> 18174 bytes
-rw-r--r--test/fixtures/local_glyphs/ping_fang_qt/expected.pngbin25026 -> 28524 bytes
-rw-r--r--test/fixtures/resources/glyphs-12244-12543.pbfbin0 -> 134697 bytes
-rw-r--r--test/geometry/dem_data.test.cpp8
-rw-r--r--test/gl/bucket.test.cpp9
-rw-r--r--test/gl/context.test.cpp2
-rw-r--r--test/include/mbgl/test.hpp4
-rw-r--r--test/include/mbgl/test/util.hpp (renamed from test/src/mbgl/test/util.hpp)0
-rw-r--r--test/map/map.test.cpp51
-rw-r--r--test/renderer/image_manager.test.cpp12
-rw-r--r--test/src/mbgl/test/sqlite3_test_fs.cpp1
-rw-r--r--test/src/mbgl/test/stub_map_observer.hpp6
-rw-r--r--test/storage/offline_database.test.cpp41
-rw-r--r--test/storage/offline_download.test.cpp91
-rw-r--r--test/storage/sync_file_source.test.cpp50
-rw-r--r--test/style/conversion/geojson_options.test.cpp12
-rw-r--r--test/style/conversion/light.test.cpp23
-rw-r--r--test/style/expression/expression.test.cpp3
-rw-r--r--test/style/filter.test.cpp7
-rw-r--r--test/test-files.json8
-rw-r--r--test/text/cross_tile_symbol_index.test.cpp31
-rw-r--r--test/text/glyph_manager.test.cpp53
-rw-r--r--test/text/local_glyph_rasterizer.test.cpp22
-rw-r--r--test/text/quads.test.cpp168
-rw-r--r--test/text/shaping.test.cpp5
-rw-r--r--test/util/peer.test.cpp189
-rw-r--r--test/util/tile_cover.test.cpp14
m---------vendor/args0
-rw-r--r--vendor/args-files.json8
-rw-r--r--vendor/args.cmake5
-rw-r--r--vendor/benchmark.cmake44
-rw-r--r--vendor/boost.cmake10
-rw-r--r--vendor/cheap-ruler-cpp.cmake10
-rw-r--r--vendor/earcut.hpp.cmake10
-rw-r--r--vendor/eternal.cmake10
-rw-r--r--vendor/expected.cmake10
m---------vendor/filesystem0
-rw-r--r--vendor/filesystem-files.json13
-rw-r--r--vendor/filesystem.cmake5
-rw-r--r--vendor/geojson-vt-cpp.cmake10
m---------vendor/geojson.hpp0
-rw-r--r--vendor/geojson.hpp-files.json10
-rw-r--r--vendor/geojson.hpp.cmake5
-rw-r--r--vendor/googletest.cmake28
-rw-r--r--vendor/icu.cmake46
m---------vendor/jni.hpp0
-rw-r--r--vendor/jni.hpp-files.json34
-rw-r--r--vendor/jni.hpp.cmake5
m---------vendor/kdbush.hpp0
-rw-r--r--vendor/kdbush.hpp-files.json8
-rw-r--r--vendor/kdbush.hpp.cmake5
m---------vendor/mapbox-base0
-rw-r--r--vendor/mapbox-base-files.json121
-rw-r--r--vendor/nunicode.cmake28
m---------vendor/pixelmatch-cpp0
-rw-r--r--vendor/pixelmatch-cpp-files.json8
-rw-r--r--vendor/pixelmatch-cpp.cmake5
-rw-r--r--vendor/polylabel.cmake10
-rw-r--r--vendor/protozero.cmake10
m---------vendor/rapidjson0
-rw-r--r--vendor/rapidjson-files.json42
-rw-r--r--vendor/rapidjson.cmake15
-rw-r--r--vendor/shelf-pack-cpp.cmake10
-rw-r--r--vendor/sqlite.cmake26
m---------vendor/supercluster.hpp0
-rw-r--r--vendor/supercluster.hpp-files.json8
-rw-r--r--vendor/supercluster.hpp.cmake5
-rw-r--r--vendor/unique_resource.cmake10
-rw-r--r--vendor/vector-tile.cmake10
-rw-r--r--vendor/wagyu.cmake10
551 files changed, 13776 insertions, 5622 deletions
diff --git a/.clang-format b/.clang-format
index ff0f39e73b..cacdf25308 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,18 +1,9 @@
-Standard: Cpp11
-IndentWidth: 4
+---
+Language: Cpp
+BasedOnStyle: Google
AccessModifierOffset: -4
-UseTab: Never
-BinPackParameters: false
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-AllowShortBlocksOnASingleLine: false
-AllowShortFunctionsOnASingleLine: false
-ConstructorInitializerAllOnOneLineOrOnePerLine: true
-AlwaysBreakTemplateDeclarations: true
-NamespaceIndentation: None
-PointerBindsToType: true
-SpacesInParentheses: false
-BreakBeforeBraces: Attach
-ColumnLimit: 100
-Cpp11BracedListStyle: false
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 120
+IndentWidth: 4
SpacesBeforeTrailingComments: 1
+...
diff --git a/.clang-tidy b/.clang-tidy
index 97600637ed..e8a74962be 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,3 +1,4 @@
-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'
+Checks: 'bugprone-*,clang-analyzer-*,cppcoreguidelines-*,google-*,misc-*,modernize-*,performance-*,readability-*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-google-readability-todo,-google-runtime-int,-misc-non-private-member-variables-in-classes,-readability-magic-numbers,-readability-named-parameter'
WarningsAsErrors: '*'
+HeaderFilterRegex: '.*'
FormatStyle: file
diff --git a/.cmake-format b/.cmake-format
new file mode 100644
index 0000000000..abd1e46cb8
--- /dev/null
+++ b/.cmake-format
@@ -0,0 +1,91 @@
+
+# --------------------------
+# General Formatting Options
+# --------------------------
+# How wide to allow formatted cmake files
+line_width = 140
+
+# How many spaces to tab for indent
+tab_size = 4
+
+# If arglists are longer than this, break them always
+max_subargs_per_line = 3
+
+# If true, separate flow control names from their parentheses with a space
+separate_ctrl_name_with_space = False
+
+# If true, separate function names from parentheses with a space
+separate_fn_name_with_space = False
+
+# If a statement is wrapped to more than one line, than dangle the closing
+# parenthesis on it's own line
+dangle_parens = True
+
+# If the statement spelling length (including space and parenthesis is larger
+# than the tab width by more than this amoung, then force reject un-nested
+# layouts.
+max_prefix_chars = 2
+
+# If a candidate layout is wrapped horizontally but it exceeds this many lines,
+# then reject the layout.
+max_lines_hwrap = 8
+
+# What style line endings to use in the output.
+line_ending = u'unix'
+
+# Format command names consistently as 'lower' or 'upper' case
+command_case = u'canonical'
+
+# Format keywords consistently as 'lower' or 'upper' case
+keyword_case = u'unchanged'
+
+# Specify structure for custom cmake functions
+additional_commands = {
+ "add_node_module": {
+ "kwargs": {
+ "INSTALL_PATH": "*",
+ "NAN_VERSION": "*",
+ "EXCLUDE_NODE_ABIS": "*"
+ }
+ },
+ "target_compile_definitions": {
+ "kwargs": {
+ "PRIVATE": "*",
+ "PUBLIC": "*"
+ }
+ },
+ "target_sources": {
+ "kwargs": {
+ "INTERFACE": "*",
+ "PRIVATE": "*",
+ "PUBLIC": "*"
+ }
+ }
+}
+
+# A list of command names which should always be wrapped
+always_wrap = [
+ "add_executable",
+ "add_library",
+ "set_source_files_properties",
+ "target_compile_definitions",
+ "target_include_directories",
+ "target_link_libraries",
+ "target_sources"
+]
+
+# Specify the order of wrapping algorithms during successive reflow attempts
+algorithm_order = [0, 1, 2, 3, 4]
+
+# If true, the argument lists which are known to be sortable will be sorted
+# lexicographicall
+enable_sort = True
+
+# If true, the parsers may infer whether or not an argument list is sortable
+# (without annotation).
+autosort = True
+
+# If a comment line starts with at least this many consecutive hash characters,
+# then don't lstrip() them off. This allows for lazy hash rulers where the first
+# hash char is not separated by space
+hashruler_min_length = 10
diff --git a/.github/stale.yml b/.github/stale.yml
index 4650483cd5..6f55110173 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,7 +1,7 @@
# Configuration for probot-stale - https://github.com/probot/stale
-exemptProjects: false
-exemptMilestones: false
+exemptProjects: true
+exemptMilestones: true
staleLabel: archived
diff --git a/.gitignore b/.gitignore
index e5a7cd9d21..73e87815a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*.gcda
*~
offline.db
+new_offline.db
xcuserdata
/platform/node/test/actual
/platform/node/test/diff
diff --git a/.gitmodules b/.gitmodules
index fd2e80d4bb..ff48ca17c3 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,18 +7,12 @@
[submodule "vendor/nunicode"]
path = vendor/nunicode
url = https://bitbucket.org/alekseyt/nunicode.git
-[submodule "vendor/rapidjson"]
- path = vendor/rapidjson
- url = https://github.com/Tencent/rapidjson.git
[submodule "vendor/earcut.hpp"]
path = vendor/earcut.hpp
url = https://github.com/mapbox/earcut.hpp.git
[submodule "vendor/geojson-vt-cpp"]
path = vendor/geojson-vt-cpp
url = https://github.com/mapbox/geojson-vt-cpp.git
-[submodule "vendor/geojson.hpp"]
- path = vendor/geojson.hpp
- url = https://github.com/mapbox/geojson.hpp.git
[submodule "vendor/polylabel"]
path = vendor/polylabel
url = https://github.com/mapbox/polylabel.git
@@ -28,9 +22,6 @@
[submodule "vendor/shelf-pack-cpp"]
path = vendor/shelf-pack-cpp
url = https://github.com/mapbox/shelf-pack-cpp.git
-[submodule "vendor/supercluster.hpp"]
- path = vendor/supercluster.hpp
- url = https://github.com/mapbox/supercluster.hpp.git
[submodule "vendor/vector-tile"]
path = vendor/vector-tile
url = https://github.com/mapbox/vector-tile.git
@@ -40,9 +31,6 @@
[submodule "vendor/expected-lite"]
path = vendor/expected
url = https://github.com/martinmoene/expected-lite.git
-[submodule "vendor/kdbush.hpp"]
- path = vendor/kdbush.hpp
- url = https://github.com/mourner/kdbush.hpp.git
[submodule "vendor/unique_resource"]
path = vendor/unique_resource
url = https://github.com/okdshin/unique_resource.git
@@ -58,12 +46,6 @@
[submodule "vendor/cheap-ruler-cpp"]
path = vendor/cheap-ruler-cpp
url = https://github.com/mapbox/cheap-ruler-cpp.git
-[submodule "vendor/pixelmatch-cpp"]
- path = vendor/pixelmatch-cpp
- url = https://github.com/mapbox/pixelmatch-cpp.git
-[submodule "vendor/jni.hpp"]
- path = vendor/jni.hpp
- url = https://github.com/mapbox/jni.hpp.git
[submodule "vendor/glfw"]
path = vendor/glfw
url = https://github.com/glfw/glfw.git
@@ -82,18 +64,12 @@
[submodule "platform/darwin/docs/theme"]
path = platform/darwin/docs/theme
url = https://github.com/mapbox/jazzy-theme.git
-[submodule "vendor/args"]
- path = vendor/args
- url = https://github.com/Taywee/args
[submodule "vendor/mvt-fixtures"]
path = vendor/mvt-fixtures
url = https://github.com/mapbox/mvt-fixtures.git
[submodule "vendor/mapbox-gl-styles"]
path = vendor/mapbox-gl-styles
url = https://github.com/mapbox/mapbox-gl-styles.git
-[submodule "vendor/filesystem"]
- path = vendor/filesystem
- url = https://github.com/gulrak/filesystem.git
[submodule "vendor/mapbox-base"]
path = vendor/mapbox-base
url = https://github.com/mapbox/mapbox-base.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index df2e4940fd..9afded1deb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,7 +22,6 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.11)
-option(WITH_CXX11ABI "Use the C++11 ABI" ON)
option(WITH_COVERAGE "Enable coverage reports" OFF)
option(WITH_OSMESA "Use OSMesa headless backend" OFF)
option(WITH_EGL "Use EGL backend" OFF)
@@ -40,13 +39,6 @@ include(cmake/mason.cmake)
include(cmake/xcode.cmake)
include(cmake/doxygen.cmake)
-if(WITH_CXX11ABI)
- set(MASON_CXXABI_SUFFIX -cxx11abi)
- add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
-else()
- add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
-endif()
-
if(WITH_OSMESA AND WITH_EGL)
message(FATAL_ERROR "WITH_OSMESA and WITH_EGL are mutually exclusive.")
endif()
@@ -185,6 +177,7 @@ include(cmake/core.cmake)
if(COMMAND mbgl_platform_test)
include(cmake/test.cmake)
include(cmake/render-test.cmake)
+ include(cmake/expression-test.cmake)
endif()
if(COMMAND mbgl_platform_benchmark)
diff --git a/Makefile b/Makefile
index 5a127471c9..1c1199932e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,9 @@
export BUILDTYPE ?= Debug
export IS_LOCAL_DEVELOPMENT ?= true
-export WITH_CXX11ABI ?= $(shell scripts/check-cxx11abi.sh)
export TARGET_BRANCH ?= master
+CMAKE ?= cmake
+
ifeq ($(BUILDTYPE), Release)
else ifeq ($(BUILDTYPE), RelWithDebInfo)
@@ -84,7 +85,7 @@ endif
$(MACOS_PROJ_PATH): $(BUILD_DEPS) $(MACOS_USER_DATA_PATH)/WorkspaceSettings.xcsettings
mkdir -p $(MACOS_OUTPUT_PATH)
- (cd $(MACOS_OUTPUT_PATH) && cmake -G Xcode ../.. \
+ (cd $(MACOS_OUTPUT_PATH) && $(CMAKE) -G Xcode ../.. \
-DWITH_EGL=${WITH_EGL})
$(MACOS_USER_DATA_PATH)/WorkspaceSettings.xcsettings: platform/macos/WorkspaceSettings.xcsettings
@@ -183,7 +184,7 @@ genstrings:
$(MACOS_COMPDB_PATH)/Makefile:
mkdir -p $(MACOS_COMPDB_PATH)
- (cd $(MACOS_COMPDB_PATH) && cmake ../../../.. \
+ (cd $(MACOS_COMPDB_PATH) && $(CMAKE) ../../../.. \
-DCMAKE_BUILD_TYPE=$(BUILDTYPE) \
-DWITH_EGL=${WITH_EGL})
@@ -274,7 +275,7 @@ endif
$(IOS_PROJ_PATH): $(IOS_USER_DATA_PATH)/WorkspaceSettings.xcsettings $(BUILD_DEPS)
mkdir -p $(IOS_OUTPUT_PATH)
- (cd $(IOS_OUTPUT_PATH) && cmake -G Xcode ../.. \
+ (cd $(IOS_OUTPUT_PATH) && $(CMAKE) -G Xcode ../.. \
-DCMAKE_TOOLCHAIN_FILE=../../platform/ios/toolchain.cmake \
-DMBGL_PLATFORM=ios \
-DMASON_PLATFORM=ios)
@@ -374,9 +375,8 @@ LINUX_BUILD = $(LINUX_OUTPUT_PATH)/build.ninja
$(LINUX_BUILD): $(BUILD_DEPS)
mkdir -p $(LINUX_OUTPUT_PATH)
- (cd $(LINUX_OUTPUT_PATH) && cmake -G Ninja ../../.. \
+ (cd $(LINUX_OUTPUT_PATH) && $(CMAKE) -G Ninja ../../.. \
-DCMAKE_BUILD_TYPE=$(BUILDTYPE) \
- -DWITH_CXX11ABI=${WITH_CXX11ABI} \
-DWITH_COVERAGE=${WITH_COVERAGE} \
-DWITH_OSMESA=${WITH_OSMESA} \
-DWITH_EGL=${WITH_EGL})
@@ -453,111 +453,13 @@ check: compdb
endif
-#### Qt targets #####################################################
-
-QT_QMAKE_FOUND := $(shell command -v qmake 2> /dev/null)
-ifdef QT_QMAKE_FOUND
- export QT_INSTALL_DOCS = $(shell qmake -query QT_INSTALL_DOCS)
- QT_ROOT_PATH = build/qt-$(BUILD_PLATFORM)-$(BUILD_PLATFORM_VERSION)
-endif
-
-export QT_OUTPUT_PATH = $(QT_ROOT_PATH)/$(BUILDTYPE)
-QT_BUILD = $(QT_OUTPUT_PATH)/build.ninja
-
-$(QT_BUILD): $(BUILD_DEPS)
- @scripts/check-qt.sh
- mkdir -p $(QT_OUTPUT_PATH)
- (cd $(QT_OUTPUT_PATH) && cmake -G Ninja ../../.. \
- -DCMAKE_BUILD_TYPE=$(BUILDTYPE) \
- -DMBGL_PLATFORM=qt \
- -DMASON_PLATFORM=$(MASON_PLATFORM) \
- -DMASON_PLATFORM_VERSION=$(MASON_PLATFORM_VERSION) \
- -DWITH_QT_DECODERS=${WITH_QT_DECODERS} \
- -DWITH_QT_I18N=${WITH_QT_I18N} \
- -DWITH_CXX11ABI=${WITH_CXX11ABI} \
- -DWITH_COVERAGE=${WITH_COVERAGE})
-
-ifeq ($(HOST_PLATFORM), macos)
-
-MACOS_QT_PROJ_PATH = $(QT_ROOT_PATH)/xcode/mbgl.xcodeproj
-$(MACOS_QT_PROJ_PATH): $(BUILD_DEPS)
- @scripts/check-qt.sh
- mkdir -p $(QT_ROOT_PATH)/xcode
- (cd $(QT_ROOT_PATH)/xcode && cmake -G Xcode ../../.. \
- -DMBGL_PLATFORM=qt \
- -DMASON_PLATFORM=$(MASON_PLATFORM) \
- -DMASON_PLATFORM_VERSION=$(MASON_PLATFORM_VERSION) \
- -DWITH_QT_DECODERS=${WITH_QT_DECODERS} \
- -DWITH_QT_I18N=${WITH_QT_I18N} \
- -DWITH_CXX11ABI=${WITH_CXX11ABI} \
- -DWITH_COVERAGE=${WITH_COVERAGE})
-
-.PHONY: qtproj
-qtproj: $(MACOS_QT_PROJ_PATH)
- open $(MACOS_QT_PROJ_PATH)
-
-endif
-
-ifdef QNX_HOST
-export WITH_QT_DECODERS ?= ON
-export QCC_COMPILER_TARGET ?= gcc_ntox86_64
-export QCC_NTOARCH ?= x86_64
-
-export QNX_OUTPUT_PATH = build/qt-qnx-$(QCC_NTOARCH)/$(BUILDTYPE)
-QNX_QT_BUILD = $(QNX_OUTPUT_PATH)/build.ninja
-$(QNX_QT_BUILD): $(BUILD_DEPS)
- @scripts/check-qt.sh
- mkdir -p $(QNX_OUTPUT_PATH)
- (cd $(QNX_OUTPUT_PATH) && cmake -G Ninja ../../.. \
- -DCMAKE_BUILD_TYPE=$(BUILDTYPE) \
- -DQCC_COMPILER_TARGET=${QCC_COMPILER_TARGET} \
- -DQCC_NTOARCH=${QCC_NTOARCH} \
- -DCMAKE_TOOLCHAIN_FILE=platform/qt/qnx.cmake \
- -DMBGL_PLATFORM=qt \
- -DWITH_QT_DECODERS=${WITH_QT_DECODERS} \
- -DWITH_QT_I18N=${WITH_QT_I18N} \
- -DWITH_CXX11ABI=${WITH_CXX11ABI} \
- -DWITH_COVERAGE=${WITH_COVERAGE})
-
-.PHONY: qnx-qt-lib
-qnx-qt-lib: $(QNX_QT_BUILD)
- $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(QNX_OUTPUT_PATH) qmapboxgl
-
-endif
-
-.PHONY: qt-lib
-qt-lib: $(QT_BUILD)
- $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(QT_OUTPUT_PATH) qmapboxgl
-
-.PHONY: qt-app
-qt-app: $(QT_BUILD)
- $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(QT_OUTPUT_PATH) mbgl-qt
-
-.PHONY: run-qt-app
-run-qt-app: qt-app
- $(QT_OUTPUT_PATH)/mbgl-qt
-
-.PHONY: qt-test
-qt-test: $(QT_BUILD)
- $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(QT_OUTPUT_PATH) mbgl-test
-
-run-qt-test-%: qt-test
- $(QT_OUTPUT_PATH)/mbgl-test --gtest_catch_exceptions=0 --gtest_filter=$*
-
-.PHONY: run-qt-test
-run-qt-test: run-qt-test-*
-
-.PHONY: qt-docs
-qt-docs:
- qdoc $(shell pwd)/platform/qt/config.qdocconf -outputdir $(shell pwd)/$(QT_OUTPUT_PATH)/docs
-
#### Node targets ##############################################################
.PHONY: test-node
test-node: node
npm test
npm run test-query
- npm run test-expressions
+ npm run test-memory
#### Android targets ###########################################################
@@ -798,7 +700,7 @@ test-code-android:
# Runs checkstyle and lint on the java code
.PHONY: android-check
-android-check : android-checkstyle run-android-nitpick android-lint-sdk android-lint-test-app android-ktlint
+android-check : android-ktlint android-checkstyle android-lint-sdk android-lint-test-app run-android-nitpick
# Runs checkstyle on the java code
.PHONY: android-checkstyle
@@ -807,7 +709,7 @@ android-checkstyle: platform/android/gradle/configuration.gradle
# Runs checkstyle on the kotlin code
.PHONY: android-ktlint
-android-ktlint:
+android-ktlint: platform/android/gradle/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none ktlint
# Runs lint on the Android SDK java code
diff --git a/appveyor.yml b/appveyor.yml
index 0e1bd01124..2440c3f601 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -5,29 +5,15 @@ image:
- Visual Studio 2017
environment:
- PYTHON: C:\python37
+ PYTHON: C:\python37
matrix:
- configuration: Release
-
-clone_depth: 1
-
-after_build:
- - mkdir qmapboxgl
- - mkdir qmapboxgl\lib
- - mkdir qmapboxgl\include
- - copy qmapboxgl.dll qmapboxgl\lib
- - copy qmapboxgl.exp qmapboxgl\lib
- - copy qmapboxgl.lib qmapboxgl\lib
- - copy %APPVEYOR_BUILD_FOLDER%\platform\qt\include\* qmapboxgl\include
- - 7z a qmapboxgl-%APPVEYOR_REPO_COMMIT%.zip qmapboxgl
-
-artifacts:
- - path: build\qmapboxgl-%APPVEYOR_REPO_COMMIT%.zip
- name: QMapboxGL
+ - configuration: Debug
test: off
+clone_depth: 1
for:
-
@@ -36,10 +22,10 @@ for:
- image: Visual Studio 2015
environment:
- LLVM_VERSION: 5.0.1
- LLVM_HASH: 981543611D719624ACB29A2CFFD6A479CFF36E8AB5EE8A57D8ECA4F9C4C6956F
- VCVARSALL: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat'
- QT_PREFIX: 'C:\Qt\latest\msvc2015_64\lib\cmake'
+ LLVM_VERSION: 5.0.1
+ LLVM_HASH: 981543611D719624ACB29A2CFFD6A479CFF36E8AB5EE8A57D8ECA4F9C4C6956F
+ VCVARSALL: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat'
+ QT_PREFIX: 'C:\Qt\latest\msvc2015_64\lib\cmake'
-
matrix:
@@ -53,52 +39,52 @@ for:
QT_PREFIX: 'C:\Qt\latest\msvc2017_64\lib\cmake'
cache:
- - '%APPVEYOR_BUILD_FOLDER%\mason_packages'
+ - '%APPVEYOR_BUILD_FOLDER%\.git\modules'
- '%APPVEYOR_BUILD_FOLDER%\LLVM-%LLVM_VERSION%-win64.exe'
- '%APPVEYOR_BUILD_FOLDER%\cmake-3.10.1-win64-x64.zip'
- C:\clcache
install:
+ - set PATH=%PYTHON%\Scripts;%PYTHON%;%PATH%
+ - pip install clcache
- git config --system core.longpaths true
- git submodule sync
- git submodule update --init
- git submodule foreach git submodule update --init
- ps: |
- if (!(Test-Path cmake-3.10.1-win64-x64.zip)) {
- appveyor DownloadFile https://cmake.org/files/v3.10/cmake-3.10.1-win64-x64.zip
- }
- scripts\check-sha256.ps1 cmake-3.10.1-win64-x64.zip 8251F70C85B58F3CA1F24E4A3B0637E2D609B5E4A341D00B70E02E89244D5029
- Start-Process -FilePath '7z' -ArgumentList 'x','cmake-3.10.1-win64-x64.zip','-oC:\' -Wait
- - ps: |
if (!(Test-Path "LLVM-$env:LLVM_VERSION-win64.exe")) {
appveyor DownloadFile "https://releases.llvm.org/$env:LLVM_VERSION/LLVM-$env:LLVM_VERSION-win64.exe"
}
scripts\check-sha256.ps1 LLVM-$env:LLVM_VERSION-win64.exe "$env:LLVM_HASH"
Start-Process -FilePath "LLVM-$env:LLVM_VERSION-win64.exe" -ArgumentList '/S',"/D=C:\LLVM-$env:LLVM_VERSION" -Wait
- - set PATH=%PYTHON%\Scripts;%PYTHON%;%PATH%
- - pip install clcache
+ - ps: |
+ if (!(Test-Path cmake-3.10.1-win64-x64.zip)) {
+ appveyor DownloadFile https://cmake.org/files/v3.10/cmake-3.10.1-win64-x64.zip
+ }
+ scripts\check-sha256.ps1 cmake-3.10.1-win64-x64.zip 8251F70C85B58F3CA1F24E4A3B0637E2D609B5E4A341D00B70E02E89244D5029
+ Start-Process -FilePath '7z' -ArgumentList 'x','cmake-3.10.1-win64-x64.zip','-oC:\' -Wait
before_build:
- set PATH=C:\LLVM-%LLVM_VERSION%\bin;%PATH%
- set PATH=C:\cmake-3.10.1-win64-x64\bin;%PATH%
- set CLCACHE_DIR=c:\clcache
- set CLCACHE_CL=clang-cl
+ - clcache -z
- set CC=clcache
- set CXX=clcache
+ - set CTEST_OUTPUT_ON_FAILURE=1
- mkdir %APPVEYOR_BUILD_FOLDER%\build
- cd %APPVEYOR_BUILD_FOLDER%\build
build_script:
- call "%VCVARSALL%" amd64
+ - cmake -E remove ../next/vendor
+ - cmake -E copy_directory ../vendor ../next/vendor
- cmake -G "Ninja"
- -DMBGL_PLATFORM=qt
- -DWITH_QT_DECODERS=ON
- -DWITH_QT_I18N=ON
- -DWITH_NODEJS=OFF
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_PREFIX_PATH=%QT_PREFIX%
- -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe"
- ..
- - clcache -z
- - cmake --build . -- -j %NUMBER_OF_PROCESSORS%
+ -DCMAKE_BUILD_TYPE=%CONFIGURATION%
+ -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe"
+ -DCMAKE_PREFIX_PATH=%QT_PREFIX%
+ -DMBGL_WITH_QT=ON
+ ../next
+ - cmake --build . --target qmapboxgl -- -j %NUMBER_OF_PROCESSORS%
- clcache -s
diff --git a/benchmark/fixtures/api/cache.db b/benchmark/fixtures/api/cache.db
index e41174548d..fc51eca3b9 100644
--- a/benchmark/fixtures/api/cache.db
+++ b/benchmark/fixtures/api/cache.db
Binary files differ
diff --git a/benchmark/include/mbgl/benchmark.hpp b/benchmark/include/mbgl/benchmark.hpp
index 1a9904de51..f68c847a74 100644
--- a/benchmark/include/mbgl/benchmark.hpp
+++ b/benchmark/include/mbgl/benchmark.hpp
@@ -1,7 +1,9 @@
#pragma once
+#include <mbgl/util/util.hpp>
+
namespace mbgl {
-int runBenchmark(int argc, char* argv[]);
+MBGL_EXPORT int runBenchmark(int argc, char* argv[]);
} // namespace mbgl
diff --git a/bin/offline.cpp b/bin/offline.cpp
index fc8b0f1bd4..fcc6adc3ef 100644
--- a/bin/offline.cpp
+++ b/bin/offline.cpp
@@ -217,6 +217,8 @@ int main(int argc, char *argv[]) {
std::cout << status.completedResourceCount << " / " << status.requiredResourceCount
<< " resources"
+ << status.completedTileCount << " / " << status.requiredTileCount
+ << "tiles"
<< (status.requiredResourceCountIsPrecise ? "; " : " (indeterminate); ")
<< status.completedResourceSize << " bytes downloaded"
<< " (" << bytesPerSecond << " bytes/sec)"
diff --git a/circle.yml b/circle.yml
index 07d591e6c0..53f3c79c3a 100644
--- a/circle.yml
+++ b/circle.yml
@@ -2,13 +2,143 @@ version: 2.1
workflows:
version: 2
- default:
+ mbgl-next:
jobs:
+ - next-sanity-checks
+ - next-build-template:
+ name: next-android-armeabi-v7a-release
+ executor_name: ubuntu-disco
+ target_is_android: true
+ requires:
+ - next-sanity-checks
+ config_params: '-G Ninja -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=armeabi-v7a'
+ - next-build-template:
+ name: next-android-arm64-v8a-release
+ executor_name: ubuntu-disco
+ target_is_android: true
+ requires:
+ - next-android-armeabi-v7a-release
+ config_params: '-G Ninja -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=arm64-v8a'
+ - next-build-template:
+ name: next-android-x86-release
+ executor_name: ubuntu-disco
+ target_is_android: true
+ requires:
+ - next-android-armeabi-v7a-release
+ config_params: '-G Ninja -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=x86'
+ - next-build-template:
+ name: next-android-x86_64-release
+ executor_name: ubuntu-disco
+ target_is_android: true
+ requires:
+ - next-android-armeabi-v7a-release
+ config_params: '-G Ninja -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=x86_64'
+ - next-build-template:
+ name: next-linux-gcc8-release
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-sanity-checks
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8'
+ - next-build-template:
+ name: next-linux-gcc4.9-release
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-linux-gcc8-release
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-4.9 -DCMAKE_CXX_COMPILER=g++-4.9'
+ build_params: '--target mbgl-glfw'
+ test_params: '-N -Q'
+ - next-build-template:
+ name: next-linux-gcc8-debug-coverage
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-linux-gcc8-release
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_BUILD_TYPE=DebugCoverage'
+ - next-build-template:
+ name: next-linux-clang8-release
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-sanity-checks
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8'
+ - next-build-template:
+ name: next-linux-asan
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-linux-gcc8-debug-coverage
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_BUILD_TYPE=Sanitize -DMBGL_WITH_SANITIZER=address'
+ - next-build-template:
+ name: next-linux-tsan
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-linux-gcc8-debug-coverage
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_BUILD_TYPE=Sanitize -DMBGL_WITH_SANITIZER=thread'
+ - next-build-template:
+ name: next-linux-memsan
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-linux-gcc8-debug-coverage
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_BUILD_TYPE=Sanitize -DMBGL_WITH_SANITIZER=memory'
+ - next-build-template:
+ name: next-linux-ubsan
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-linux-gcc8-debug-coverage
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_BUILD_TYPE=Sanitize -DMBGL_WITH_SANITIZER=undefined'
+ - next-build-template:
+ name: next-qt5-linux-gcc5-release
+ executor_name: ubuntu-disco
+ target_is_linux: true
+ requires:
+ - next-sanity-checks
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-5 -DCMAKE_CXX_COMPILER=g++-5 -DMBGL_WITH_QT=ON'
+ - next-build-template:
+ name: next-qt5-macos-gcc5-release
+ executor_name: macos-11_0_0
+ target_is_macos: true
+ requires:
+ - next-qt5-linux-gcc5-release
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DMBGL_WITH_QT=ON -DCMAKE_PREFIX_PATH=$(echo /usr/local/Cellar/qt/5.*/lib/cmake)'
+ test_params: '-N -Q'
+ - next-build-template:
+ name: next-macos-xcode11-release
+ executor_name: macos-11_0_0
+ target_is_macos: true
+ requires:
+ - next-sanity-checks
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache'
+ - next-build-template:
+ name: next-macos-xcode11-debug
+ executor_name: macos-11_0_0
+ target_is_macos: true
+ requires:
+ - next-macos-xcode11-release
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Debug'
+ - next-build-template:
+ name: next-ios-xcode11-release
+ executor_name: macos-11_0_0
+ target_is_macos: true
+ requires:
+ - next-sanity-checks
+ config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_SYSROOT=iphonesimulator'
+ test_params: '-Q -N'
+ mbgl-legacy:
+ jobs:
+ #
+ # Naming convention: {platform}-{additional description}-{build type}
+ # - {platform} is the client platform/framework, which may differ from
+ # the build platform. Specify both if applicable, e.g., "qt5-macos".
+ # - {additional description} optionally describes the compiler or other
+ # unique aspect of the build environment.
+ # - {build type} is typically "debug" or "release".
+ #
- nitpick
- - clang-tidy:
- filters:
- branches:
- ignore: master
- android-debug-arm-v7-buck
- android-arm-template:
name: android-debug-arm-v8
@@ -39,12 +169,11 @@ workflows:
name: linux-clang-3.8-libcxx-debug
- linux-clang-7-sanitize-address-undefined
- linux-clang-7-sanitize-thread
- - linux-gcc49-debug:
- name: linux-gcc4.9-debug
- linux-gcc5-debug-coverage
- linux-doxygen
- linux-render-tests
- ios-debug
+ - ios-debug-xcode11
- ios-release-template:
name: ios-release
- ios-release-tag:
@@ -55,8 +184,6 @@ workflows:
ignore: /.*/
- macos-debug
- macos-render-tests
- - qt5-linux-gcc5-release
- - qt5-macos-debug
nightly:
triggers:
- schedule:
@@ -72,8 +199,104 @@ workflows:
- ios-sanitize-nightly
- ios-sanitize-address-nightly
- ios-static-analyzer-nightly
+ - ios-static-analyzer-nightly-xcode11
+
+executors:
+ ubuntu-disco:
+ docker:
+ # FIXME: Move the image to mbgl/
+ - image: tmpsantos/mbgl_ci:1.5
+ resource_class: xlarge
+ working_directory: /src
+ environment:
+ UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
+ MSAN_OPTIONS: poison_in_dtor=1
+ ASAN_OPTIONS: strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
+ QT_INSTALL_DOCS: /usr/share/qt5/doc
+ QT_VERSION: 5
+ macos-11_0_0:
+ macos:
+ xcode: '11.0.0'
+ environment:
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ HOMEBREW_NO_INSTALL_CLEANUP: 1
commands:
+ next-prepare:
+ steps:
+ - restore_cache:
+ keys:
+ - 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-{{ .Revision }}'
+ - 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-'
+ - 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-master'
+ - 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-'
+ - run:
+ name: Prepare
+ command: |
+ git submodule sync
+ git submodule update --init --recursive
+ npm install --ignore-scripts
+ ulimit -c unlimited
+ next-prepare-macos:
+ steps:
+ - run:
+ name: Prepare macOS
+ command: |
+ brew install cmake ccache glfw ninja pkgconfig qt
+ next-config:
+ parameters:
+ config_params:
+ type: string
+ steps:
+ - run:
+ name: Configure
+ command: |
+ cmake next -B build << parameters.config_params >>
+ next-build:
+ parameters:
+ build_params:
+ type: string
+ steps:
+ - run:
+ name: Build
+ command: |
+ ccache --zero-stats --max-size=2G
+ cmake --build build -j 8 << parameters.build_params >>
+ ccache --show-stats
+ git gc
+ next-save:
+ steps:
+ - save_cache:
+ key: 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-{{ .Revision }}'
+ paths:
+ - .git/modules
+ - /Users/distiller/Library/Caches/Homebrew
+ - node_modules
+ - ~/.ccache
+ - ~/.gradle
+ - run:
+ name: Collecting artifacts
+ when: on_fail
+ command: |
+ mkdir -p /tmp/tests/coredumps
+ if ls core* 1> /dev/null 2>&1; then cp core* /tmp/tests/coredumps; fi
+ mkdir -p /tmp/tests/render
+ if [ -f mapbox-gl-js/test/integration/render-tests/index.html ]; then cp mapbox-gl-js/test/integration/render-tests/index.html /tmp/tests/render; fi
+ - store_artifacts:
+ path: /tmp/tests
+ destination: tests
+ next-test:
+ parameters:
+ test_wrapper:
+ type: string
+ test_params:
+ type: string
+ steps:
+ - run:
+ name: Test
+ command: |
+ cd build
+ << parameters.test_wrapper >> ctest -j 8 --output-on-failure << parameters.test_params >>
npm-install:
steps:
- run:
@@ -250,7 +473,7 @@ commands:
command: |
mkdir -p build
cd build
- cmake -DWITH_CXX11ABI=${WITH_CXX11ABI:0} -DWITH_COVERAGE=${WITH_COVERAGE:0} -DWITH_OSMESA=${WITH_OSMESA:0} -DWITH_EGL=${WITH_EGL:0} ..
+ cmake -DWITH_COVERAGE=${WITH_COVERAGE:0} -DWITH_OSMESA=${WITH_OSMESA:0} -DWITH_EGL=${WITH_EGL:0} ..
cd ..
build-node:
steps:
@@ -262,6 +485,11 @@ commands:
- run:
name: Build mbgl-render-test
command: cmake --build build --config ${BUILDTYPE} --target mbgl-render-test -- -j${JOBS}
+ build-mbgl-expression-test:
+ steps:
+ - run:
+ name: Build mbgl-expression-test
+ command: cmake --build build --config ${BUILDTYPE} --target mbgl-expression-test -- -j${JOBS}
build-linux:
steps:
- run:
@@ -277,16 +505,6 @@ commands:
- run:
name: Build test
command: make test
- build-qt-app:
- steps:
- - run:
- name: Build qt-app
- command: make qt-app
- build-qt-test:
- steps:
- - run:
- name: Build qt-test
- command: make qt-test
build-ios-test:
steps:
- run:
@@ -356,18 +574,6 @@ commands:
brew install node@8
brew link node@8 --force --overwrite
- install-qt-macos-dependencies:
- steps:
- - run:
- name: Install Qt macOS dependencies
- command: |
- brew install qt
- brew link qt --force
- export HOMEBREW_QT5_CELLAR=$(brew --cellar qt)
- export HOMEBREW_QT5_VERSION=$(brew list --versions qt | rev | cut -d' ' -f1 | rev)
- sudo ln -s $HOMEBREW_QT5_CELLAR/$HOMEBREW_QT5_VERSION/mkspecs /usr/local/mkspecs
- sudo ln -s $HOMEBREW_QT5_CELLAR/$HOMEBREW_QT5_VERSION/plugins /usr/local/plugins
-
run-node-macos-tests:
steps:
- run:
@@ -425,6 +631,19 @@ commands:
# Unfortunately, Google Test eats the status code, so we'll have to check the output.
[ -z "$(sed -n '/^SUMMARY: .*Sanitizer:/p' sanitizer)" ]
+ run-expression-tests:
+ steps:
+ - run:
+ name: Run expression tests
+ command: |
+ build/mbgl-expression-test -s --seed=$RANDOM
+
+ upload-expression-tests:
+ steps:
+ - store_artifacts:
+ path: mapbox-gl-js/test/integration/expression-tests/index.html
+ destination: expression-tests
+
publish-node-package:
steps:
- run:
@@ -465,7 +684,115 @@ commands:
scripts/notify-slack.sh
fi
+ #
+ # Add this step to all regular jobs to enable skipping of certain non-code-related changes.
+ #
+ # Do not include this step in nightly or release deployment jobs.
+ #
+ # To make a job potentially skippable on changes unrelated to its platform, it must:
+ # - Target one of the skippable platforms: Android, iOS, or macOS.
+ # - Have a job name that begins with a supported platform name.
+ # - Not be related to core functionality or rendering tests. Job names that
+ # contain "render-tests" cannot be skipped by platform changes.
+ #
+ # See the script in the following step for how to implement support for other platforms.
+ #
+ check-if-this-job-can-be-skipped:
+ steps:
+ - run:
+ name: Check if this job can be skipped
+ command: |
+ if [[ $CIRCLE_BRANCH != master ]] && [[ $CIRCLE_BRANCH != release-* ]] && [[ -z $CIRCLE_TAG ]]; then
+ scripts/check-ci-job-skippability.js
+ fi
+
jobs:
+ next-sanity-checks:
+ executor: ubuntu-disco
+ steps:
+ - checkout
+ - next-prepare
+ - next-config:
+ config_params: '-DMBGL_WITH_CORE_ONLY=ON'
+ - run:
+ name: CMake Format
+ command: |
+ cmake-format -i $(find next -type f -name CMakeLists.txt -o -name '*.cmake')
+ git diff --exit-code
+ - run:
+ name: Clang Format
+ command: |
+ git diff -U0 --no-color origin/master... *.cpp *.hpp | clang-format-diff-8 -p1 -i
+ git diff --exit-code
+ - run:
+ name: Clang Tidy
+ command: |
+ git diff -U0 --no-color origin/master... src include | clang-tidy-diff-8.py -clang-tidy-binary clang-tidy-8 -p1 -path build
+ - run:
+ name: Code Generators
+ command: |
+ platform/android/scripts/generate-style-code.js
+ scripts/generate-file-lists.js
+ scripts/generate-shaders.js
+ scripts/generate-style-code.js
+ git add -A && git diff --staged --exit-code
+ - next-save
+ next-build-template:
+ parameters:
+ config_params:
+ type: string
+ default: ''
+ build_params:
+ type: string
+ default: ''
+ test_params:
+ type: string
+ default: ''
+ executor_name:
+ type: string
+ target_is_android:
+ type: boolean
+ default: false
+ target_is_linux:
+ type: boolean
+ default: false
+ target_is_macos:
+ type: boolean
+ default: false
+ executor: << parameters.executor_name >>
+ steps:
+ - checkout
+ - next-prepare
+ - when:
+ condition: << parameters.target_is_android >>
+ steps:
+ - next-config:
+ config_params: << parameters.config_params >>
+ - next-build:
+ build_params: << parameters.build_params >>
+ - when:
+ condition: << parameters.target_is_linux >>
+ steps:
+ - next-config:
+ config_params: << parameters.config_params >>
+ - next-build:
+ build_params: << parameters.build_params >>
+ - next-test:
+ test_wrapper: 'xvfb-run -s -noreset'
+ test_params: << parameters.test_params >>
+ - when:
+ condition: << parameters.target_is_macos >>
+ steps:
+ - next-prepare-macos
+ - next-config:
+ config_params: << parameters.config_params >>
+ - next-build:
+ build_params: << parameters.build_params >>
+ - next-test:
+ test_wrapper: ''
+ test_params: << parameters.test_params >>
+ - next-save
+
nitpick:
docker:
- image: mbgl/linux-clang-7:a5a3c52107
@@ -503,29 +830,6 @@ jobs:
when: always
# ------------------------------------------------------------------------------
- clang-tidy:
- docker:
- - image: mbgl/linux-clang-7:a5a3c52107
- working_directory: /src
- environment:
- LIBSYSCONFCPUS: 4
- JOBS: 4
- BUILDTYPE: Debug
- steps:
- - install-dependencies: { ccache: false }
- - run:
- name: Fetch 'origin/master' branch
- command: git fetch origin master:refs/remotes/origin/master
- - run:
- name: Generate compilation database
- command: make compdb
- - run:
- name: Run Clang checks
- command: make check TARGET_BRANCH=${CIRCLE_TARGET_BRANCH:master}
- no_output_timeout: 20m
- - save-dependencies: { ccache: false }
-
-# ------------------------------------------------------------------------------
android-arm-template:
parameters:
stl:
@@ -555,6 +859,7 @@ jobs:
MBGL_ANDROID_STL: << parameters.stl >>
steps:
- install-dependencies: { gradle: true }
+ - check-if-this-job-can-be-skipped
- run:
name: Initialize vendor submodules
command: git submodule update --init platform/android/vendor
@@ -631,6 +936,7 @@ jobs:
IS_LOCAL_DEVELOPMENT: false
steps:
- install-dependencies: { gradle: true }
+ - check-if-this-job-can-be-skipped
- run:
name: Initialize vendor submodules
command: git submodule update --init platform/android/vendor
@@ -729,6 +1035,9 @@ jobs:
ANDROID_NDK: /android/sdk/ndk-bundle
steps:
- checkout
+ - npm-install
+ - prepare-environment
+ - check-if-this-job-can-be-skipped
- run:
name: Checkout submodules
command: |
@@ -751,7 +1060,6 @@ jobs:
JOBS: 4
BUILDTYPE: RelWithDebInfo
WITH_EGL: 1
- WITH_CXX11ABI: 0
steps:
- install-dependencies
- build-node
@@ -772,6 +1080,7 @@ jobs:
WITH_EGL: 1
steps:
- install-dependencies
+ - check-if-this-job-can-be-skipped
- build-node
- save-dependencies
- publish-node-package
@@ -788,6 +1097,7 @@ jobs:
- install-macos-dependencies
- install-node-macos-dependencies
- install-dependencies
+ - check-if-this-job-can-be-skipped
- build-node
- save-dependencies
- run-node-macos-tests
@@ -806,9 +1116,9 @@ jobs:
JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
- WITH_CXX11ABI: 1
steps:
- install-dependencies
+ - check-if-this-job-can-be-skipped
- build-linux
- save-dependencies
@@ -830,6 +1140,7 @@ jobs:
UBSAN_OPTIONS: print_stacktrace=1:color=always:print_summary=1
steps:
- install-dependencies
+ - check-if-this-job-can-be-skipped
- setup-llvm-symbolizer
- build-test
- save-dependencies
@@ -852,36 +1163,13 @@ jobs:
TSAN_OPTIONS: color=always:print_summary=1
steps:
- install-dependencies
+ - check-if-this-job-can-be-skipped
- setup-llvm-symbolizer
- build-test
- save-dependencies
- run-unit-tests-sanitized
# ------------------------------------------------------------------------------
- linux-gcc49-debug:
- docker:
- - image: mbgl/linux-gcc-4.9:e3818a77c1
- resource_class: large
- working_directory: /src
- environment:
- LIBSYSCONFCPUS: 4
- JOBS: 4
- BUILDTYPE: Debug
- WITH_EGL: 1
- WITH_CXX11ABI: 0
- DISPLAY: :0
- steps:
- - install-dependencies
- - build-linux
- - build-benchmark
- - build-test
- - save-dependencies
- - run-unit-tests
- - run:
- name: Build offline CLI
- command: make offline
-
-# ------------------------------------------------------------------------------
linux-gcc5-debug-coverage:
docker:
- image: mbgl/linux-gcc-5:54f59e3ac5
@@ -895,6 +1183,7 @@ jobs:
WITH_COVERAGE: 1
steps:
- install-dependencies
+ - check-if-this-job-can-be-skipped
- build-linux
- build-benchmark
- build-test
@@ -925,6 +1214,7 @@ jobs:
WITH_COVERAGE: 1
steps:
- install-dependencies
+ - check-if-this-job-can-be-skipped
- run:
name: Install doxygen
command: apt update && apt install -y doxygen
@@ -949,10 +1239,14 @@ jobs:
WITH_EGL: 1
steps:
- install-dependencies
+ - check-if-this-job-can-be-skipped
- configure-cmake
+ - build-mbgl-expression-test
- build-mbgl-render-test
+ - run-expression-tests
- run-linux-render-tests
- save-dependencies
+ - upload-expression-tests
- upload-render-tests
# ------------------------------------------------------------------------------
@@ -966,14 +1260,15 @@ jobs:
steps:
- install-macos-dependencies
- install-dependencies
- - build-ios-test
- check-public-symbols
- run:
- name: Check symbol namespacing for mapbox-events-ios
- command: make ios-check-events-symbols
- - run:
name: Lint podspecs and plist files
command: make ios-lint
+ - check-if-this-job-can-be-skipped
+ - build-ios-test
+ - run:
+ name: Check symbol namespacing for mapbox-events-ios
+ command: make ios-check-events-symbols
- run:
name: Nitpick Darwin code generation
command: scripts/nitpick/generated-code.js darwin
@@ -982,19 +1277,31 @@ jobs:
- upload-xcode-build-logs
# ------------------------------------------------------------------------------
- metrics-nightly:
- docker:
- - image: mbgl/linux-gcc-5:54f59e3ac5
- working_directory: /src
+ ios-debug-xcode11:
+ macos:
+ xcode: "11.0.0"
environment:
- LIBSYSCONFCPUS: 2
- JOBS: 2
+ BUILDTYPE: Debug
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ HOMEBREW_NO_INSTALL_CLEANUP: 1
steps:
+ - install-macos-dependencies
- install-dependencies
+ - check-public-symbols
- run:
- name: Collect GitHub statistics
- command: |
- scripts/publish_github_stats.js
+ name: Lint podspecs and plist files
+ command: make ios-lint
+ - check-if-this-job-can-be-skipped
+ - build-ios-test
+ - run:
+ name: Check symbol namespacing for mapbox-events-ios
+ command: make ios-check-events-symbols
+ - run:
+ name: Nitpick Darwin code generation
+ command: scripts/nitpick/generated-code.js darwin
+ - save-dependencies
+ - collect-xcode-build-logs
+ - upload-xcode-build-logs
# ------------------------------------------------------------------------------
ios-sanitize-nightly:
@@ -1061,6 +1368,26 @@ jobs:
- notify-slack-nightly-failure
# ------------------------------------------------------------------------------
+ ios-static-analyzer-nightly-xcode11:
+ macos:
+ xcode: "11.0.0"
+ environment:
+ BUILDTYPE: Debug
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ HOMEBREW_NO_INSTALL_CLEANUP: 1
+ SLACK_CHANNEL: C0ACM9Q2C
+ steps:
+ - install-macos-dependencies
+ - install-dependencies
+ - run:
+ name: Build and run SDK unit tests with the static analyzer
+ command: make ios-static-analyzer
+ - save-dependencies
+ - collect-xcode-build-logs
+ - upload-xcode-build-logs
+ - notify-slack-nightly-failure
+
+# ------------------------------------------------------------------------------
ios-release-template:
macos:
xcode: "10.3.0"
@@ -1072,6 +1399,7 @@ jobs:
steps:
- install-macos-dependencies
- install-dependencies
+ - check-if-this-job-can-be-skipped
- install-ios-packaging-dependencies
- run:
name: Build dynamic framework for device and simulator
@@ -1171,6 +1499,7 @@ jobs:
steps:
- install-macos-dependencies
- install-dependencies
+ - check-if-this-job-can-be-skipped
- build-macos-test
- check-public-symbols
- run:
@@ -1198,57 +1527,27 @@ jobs:
steps:
- install-macos-dependencies
- install-dependencies
+ - check-if-this-job-can-be-skipped
- configure-cmake
+ - build-mbgl-expression-test
- build-mbgl-render-test
- save-dependencies
+ - run-expression-tests
- run-macos-render-tests
+ - upload-expression-tests
- upload-render-tests
# ------------------------------------------------------------------------------
- qt5-linux-gcc5-release:
+ metrics-nightly:
docker:
- - image: mbgl/linux-gcc-5-qt-5.9:5132cfd29f
- resource_class: large
+ - image: mbgl/linux-gcc-5:54f59e3ac5
working_directory: /src
environment:
- LIBSYSCONFCPUS: 4
- JOBS: 4
- BUILDTYPE: Release
- WITH_QT_I18N: 1
+ LIBSYSCONFCPUS: 2
+ JOBS: 2
steps:
- install-dependencies
- - build-qt-app
- - build-qt-test
- - run:
- name: Build qt-docs
- command: make qt-docs
- - save-dependencies
- run:
- name: Run valgrind-backed tests
- environment:
- JOBS: 1 # https://github.com/mapbox/mapbox-gl-native/issues/9108
+ name: Collect GitHub statistics
command: |
- xvfb-run --server-args="-screen 0 1024x768x24" \
- build/qt-linux-x86_64/Release/mbgl-test --gtest_filter=-*.Load --gtest_filter=-Memory.Vector --gtest_filter=-BiDi.*
-
-# ------------------------------------------------------------------------------
- qt5-macos-debug:
- macos:
- xcode: "9.3.1"
- environment:
- BUILDTYPE: Debug
- HOMEBREW_NO_AUTO_UPDATE: 1
- HOMEBREW_NO_INSTALL_CLEANUP: 1
- steps:
- - install-macos-dependencies
- - install-qt-macos-dependencies
- - install-dependencies
- - build-qt-app
- - build-qt-test
- - run:
- name: Run qt-test
- command: make run-qt-test
- - save-dependencies
- - store_artifacts:
- path: test/fixtures
- destination: test/fixtures
+ scripts/publish_github_stats.js
diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake
index a944c64d05..7bca681f86 100644
--- a/cmake/benchmark.cmake
+++ b/cmake/benchmark.cmake
@@ -11,7 +11,7 @@ target_include_directories(mbgl-benchmark
target_link_libraries(mbgl-benchmark
PRIVATE mbgl-core
- PRIVATE benchmark
+ PRIVATE mbgl-vendor-benchmark
)
mbgl_platform_benchmark()
diff --git a/cmake/core.cmake b/cmake/core.cmake
index abb55d174a..342731de9f 100644
--- a/cmake/core.cmake
+++ b/cmake/core.cmake
@@ -8,33 +8,31 @@ target_include_directories(mbgl-core
)
target_link_libraries(mbgl-core PRIVATE
- earcut.hpp
- eternal
- expected
- geojson-vt-cpp
- kdbush.hpp
- shelf-pack-cpp
- supercluster.hpp
- unique_resource
- wagyu
+ mbgl-vendor-earcut.hpp
+ mbgl-vendor-eternal
+ mbgl-vendor-expected
+ mbgl-vendor-geojson-vt-cpp
+ mbgl-vendor-shelf-pack-cpp
+ mbgl-vendor-unique_resource
+ mbgl-vendor-wagyu
)
# linux uses ICU from mason, other platforms use vendored ICU
if(NOT MBGL_PLATFORM STREQUAL "linux")
- set(ICU_LIBRARY "icu")
+ set(ICU_LIBRARY "mbgl-vendor-icu")
endif()
# FIXME: We should not leak these many
# libraries in our public interface.
target_link_libraries(mbgl-core PUBLIC
- boost
+ mbgl-vendor-boost
+ mapbox-base-extras
mapbox-base
- geojson.hpp
${ICU_LIBRARY}
- polylabel
- protozero
- rapidjson
- vector-tile
+ mbgl-vendor-polylabel
+ mbgl-vendor-protozero
+ Mapbox::Base::Extras::rapidjson
+ mbgl-vendor-vector-tile
)
mbgl_platform_core()
diff --git a/cmake/expression-test.cmake b/cmake/expression-test.cmake
new file mode 100644
index 0000000000..6c5b71a4f6
--- /dev/null
+++ b/cmake/expression-test.cmake
@@ -0,0 +1,27 @@
+add_executable(mbgl-expression-test
+ expression-test/main.cpp
+ expression-test/expression_test_parser.cpp
+ expression-test/expression_test_runner.cpp
+ expression-test/expression_test_logger.cpp
+)
+
+if(APPLE)
+ target_link_libraries(mbgl-expression-test PRIVATE mbgl-loop-darwin)
+else()
+ target_link_libraries(mbgl-expression-test PRIVATE mbgl-loop-uv)
+endif()
+
+target_include_directories(mbgl-expression-test
+ PRIVATE src
+ PRIVATE expression-test
+ PRIVATE render-test
+)
+
+target_link_libraries(mbgl-expression-test PRIVATE
+ mbgl-core
+ Mapbox::Base::Extras::args
+ Mapbox::Base::Extras::filesystem
+ Mapbox::Base::Extras::rapidjson
+)
+
+add_definitions(-DTEST_RUNNER_ROOT_PATH="${CMAKE_SOURCE_DIR}")
diff --git a/cmake/filesource.cmake b/cmake/filesource.cmake
index ff9fa360f6..4d7febdd77 100644
--- a/cmake/filesource.cmake
+++ b/cmake/filesource.cmake
@@ -10,7 +10,7 @@ target_include_directories(mbgl-filesource
target_link_libraries(mbgl-filesource
PUBLIC mbgl-core
- PUBLIC expected
+ PUBLIC mbgl-vendor-expected
)
mbgl_filesource()
diff --git a/cmake/glfw.cmake b/cmake/glfw.cmake
index a4d9c30477..89a7768ad3 100644
--- a/cmake/glfw.cmake
+++ b/cmake/glfw.cmake
@@ -12,7 +12,6 @@ target_sources(mbgl-glfw
PRIVATE platform/glfw/glfw_renderer_frontend.cpp
PRIVATE platform/glfw/settings_json.hpp
PRIVATE platform/glfw/settings_json.cpp
- PRIVATE platform/default/include/mbgl/util/default_styles.hpp
)
target_include_directories(mbgl-glfw
@@ -22,8 +21,8 @@ target_include_directories(mbgl-glfw
target_link_libraries(mbgl-glfw
PRIVATE mbgl-core
PRIVATE glfw
- PRIVATE cheap-ruler-cpp
- PRIVATE args
+ PRIVATE mbgl-vendor-cheap-ruler-cpp
+ PRIVATE Mapbox::Base::Extras::args
)
mbgl_platform_glfw()
diff --git a/cmake/offline.cmake b/cmake/offline.cmake
index 3fa075f07b..5a6d4e04cb 100644
--- a/cmake/offline.cmake
+++ b/cmake/offline.cmake
@@ -2,17 +2,13 @@ add_executable(mbgl-offline
bin/offline.cpp
)
-target_sources(mbgl-offline
- PRIVATE platform/default/include/mbgl/util/default_styles.hpp
-)
-
target_include_directories(mbgl-offline
PRIVATE platform/default/include
)
target_link_libraries(mbgl-offline
PRIVATE mbgl-core
- PRIVATE args
+ PRIVATE Mapbox::Base::Extras::args
)
mbgl_platform_offline()
diff --git a/cmake/render-test.cmake b/cmake/render-test.cmake
index 6505cc73f8..7369655630 100644
--- a/cmake/render-test.cmake
+++ b/cmake/render-test.cmake
@@ -1,4 +1,5 @@
add_executable(mbgl-render-test
+ render-test/allocation_index.cpp
render-test/main.cpp
render-test/parser.cpp
render-test/runner.cpp
@@ -19,11 +20,11 @@ target_include_directories(mbgl-render-test
target_link_libraries(mbgl-render-test PRIVATE
mbgl-core
mbgl-filesource
- args
- expected
- filesystem
- pixelmatch-cpp
- rapidjson
+ Mapbox::Base::Extras::args
+ mbgl-vendor-expected
+ Mapbox::Base::Extras::filesystem
+ Mapbox::Base::pixelmatch-cpp
+ Mapbox::Base::Extras::rapidjson
)
add_definitions(-DTEST_RUNNER_ROOT_PATH="${CMAKE_SOURCE_DIR}")
diff --git a/cmake/render.cmake b/cmake/render.cmake
index a625b3f21f..e9c6d89c8e 100644
--- a/cmake/render.cmake
+++ b/cmake/render.cmake
@@ -8,7 +8,7 @@ target_include_directories(mbgl-render
target_link_libraries(mbgl-render
PRIVATE mbgl-core
- PRIVATE args
+ PRIVATE Mapbox::Base::Extras::args
)
mbgl_platform_render()
diff --git a/cmake/test.cmake b/cmake/test.cmake
index 1c1d3269a7..405552423c 100644
--- a/cmake/test.cmake
+++ b/cmake/test.cmake
@@ -23,12 +23,12 @@ target_include_directories(mbgl-test
)
target_link_libraries(mbgl-test PRIVATE
- googletest
- args
+ mbgl-vendor-googletest
+ Mapbox::Base::Extras::args
mbgl-core
- shelf-pack-cpp
- unique_resource
- pixelmatch-cpp
+ mbgl-vendor-shelf-pack-cpp
+ mbgl-vendor-unique_resource
+ Mapbox::Base::pixelmatch-cpp
)
mbgl_platform_test()
diff --git a/cmake/vendor.cmake b/cmake/vendor.cmake
index 0b164434c9..b996aa2b70 100644
--- a/cmake/vendor.cmake
+++ b/cmake/vendor.cmake
@@ -9,27 +9,19 @@ include(${CMAKE_SOURCE_DIR}/vendor/cheap-ruler-cpp.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/earcut.hpp.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/eternal.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/expected.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/filesystem.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/geojson-vt-cpp.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/geojson.hpp.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/googletest.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/icu.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/jni.hpp.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/kdbush.hpp.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/nunicode.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/pixelmatch-cpp.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/polylabel.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/protozero.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/rapidjson.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/shelf-pack-cpp.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/supercluster.hpp.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/unique_resource.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/vector-tile.cmake)
include(${CMAKE_SOURCE_DIR}/vendor/wagyu.cmake)
-include(${CMAKE_SOURCE_DIR}/vendor/args.cmake)
if(NOT TARGET mapbox-base)
- add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/vendor/mapbox-base ${CMAKE_BINARY_DIR}/.build-mapbox-gl-native-mapbox-base)
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/vendor/mapbox-base ${CMAKE_BINARY_DIR}/.build-mapbox-gl-native-mapbox-base EXCLUDE_FROM_ALL)
endif()
if(MBGL_PLATFORM STREQUAL "linux" OR MBGL_PLATFORM STREQUAL "macos")
diff --git a/expression-test/expression_test_logger.cpp b/expression-test/expression_test_logger.cpp
new file mode 100644
index 0000000000..9127a6d7ae
--- /dev/null
+++ b/expression-test/expression_test_logger.cpp
@@ -0,0 +1,177 @@
+#include "expression_test_logger.hpp"
+#include "expression_test_runner.hpp"
+#include "filesystem.hpp"
+
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <sstream>
+
+using namespace mbgl;
+using namespace std::literals;
+
+namespace {
+
+const char* resultsStyle = R"HTML(
+<style>
+ body { font: 18px/1.2 -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif; padding: 10px; }
+ h1 { font-size: 32px; margin-bottom: 0; }
+ button { vertical-align: middle; }
+ h2 { font-size: 24px; font-weight: normal; margin: 10px 0 10px; line-height: 1; }
+ .test { border-bottom: 1px dotted #bbb; padding-bottom: 5px; }
+ .tests { border-top: 1px dotted #bbb; margin-top: 10px; }
+ .test p, .test pre { margin: 0 0 10px; }
+ .test pre { font-size: 14px; }
+ .label { color: white; font-size: 18px; padding: 2px 6px 3px; border-radius: 3px; margin-right: 3px; vertical-align: bottom; display: inline-block; }
+ .hide { display: none; }
+ .test.failed > h2 > span { background: red; }
+ .test.passed > h2 > span { background: green; }
+ .test.ignored > h2 > span { background: grey; }
+ .test.errored > h2 > span { background: #ff006f; }
+ .test.ignored.passed > h2 > span { background: #E8A408; }
+</style>
+)HTML";
+
+const char* resultsHeaderButtons = R"HTML(
+ <button id='toggle-sequence'>Toggle test sequence</button>
+ <button id='toggle-passed'>Toggle passed tests</button>
+ <button id='toggle-ignored'>Toggle ignored tests</button>
+</h1>
+)HTML";
+
+const char* resultsScript = R"HTML(
+<script>
+document.getElementById('toggle-passed').addEventListener('click', function (e) {
+ for (const row of document.querySelectorAll('.test.passed')) {
+ row.classList.toggle('hide');
+ }
+});
+document.getElementById('toggle-ignored').addEventListener('click', function (e) {
+ for (const row of document.querySelectorAll('.test.ignored')) {
+ row.classList.toggle('hide');
+ }
+});
+document.getElementById('toggle-sequence').addEventListener('click', function (e) {
+ document.getElementById('test-sequence').classList.toggle('hide');
+});
+</script>
+)HTML";
+
+std::string createResultItem(const TestRunOutput& result, const std::string& status, bool shouldHide) {
+ std::ostringstream html;
+ html << "<div class=\"test " << status << (shouldHide ? " hide" : "") << "\">\n";
+ html << R"(<h2><span class="label">)" << status << "</span> " << result.id << "</h2>\n";
+
+ html << "<p><pre>"s << result.expression << "</pre></p>\n"s;
+ if (result.passed) {
+ html << "<strong>Serialized:</strong><p><pre>"s << result.serialized << "</pre></p>\n"s;
+ } else {
+ html << "<p><strong>Difference:</strong><pre>" << result.text << "</pre></p>\n";
+ }
+ html << "</div>\n";
+
+ return html.str();
+}
+
+std::string createResultPage(const TestStats& stats, bool shuffle, uint32_t seed) {
+ const std::size_t unsuccessfulCount = stats.errored.size() + stats.failed.size();
+ const bool unsuccessful = unsuccessfulCount > 0;
+ std::ostringstream resultsPage;
+
+ // Style
+ resultsPage << resultsStyle;
+
+ // Header with buttons
+ if (unsuccessful) {
+ resultsPage << R"HTML(<h1 style="color: red;">)HTML";
+ resultsPage << util::toString(unsuccessfulCount) << " tests failed.";
+ } else {
+ resultsPage << R"HTML(<h1 style="color: green;">)HTML";
+ resultsPage << "All tests passed!";
+ }
+
+ resultsPage << resultsHeaderButtons;
+
+ // Test sequence
+ {
+ resultsPage << "<div id='test-sequence' class='hide'>\n";
+
+ // Failed tests
+ if (unsuccessful) {
+ resultsPage << "<p><strong>Failed tests:</strong>";
+ for (const auto& failed : stats.failed) {
+ resultsPage << failed.id << " ";
+ }
+ resultsPage << "<p><strong>Errored tests:</strong>";
+ for (const auto& errored : stats.errored) {
+ resultsPage << errored.id << " ";
+ }
+ resultsPage << "</p>\n";
+ }
+
+ // Test sequence
+ resultsPage << "<p><strong>Test sequence: </strong>";
+ for (const auto& id : stats.ids) {
+ resultsPage << id << " ";
+ }
+ resultsPage << "</p>\n";
+
+ // Shuffle
+ if (shuffle) {
+ resultsPage << "<p><strong>Shuffle seed</strong>: " << util::toString(seed) << "</p>\n";
+ }
+
+ resultsPage << "</div>\n";
+ }
+
+ // Script
+ resultsPage << resultsScript;
+
+ // Tests
+ resultsPage << "<div class=\"tests\">\n";
+ const auto appendResult = [&] (const auto& results, const std::string& status, bool hide = false) {
+ for (const auto& result : results) {
+ resultsPage << createResultItem(result, status, hide);
+ }
+ };
+
+ appendResult(stats.passed, "passed"s, unsuccessful);
+ appendResult(stats.failed, "failed"s);
+ appendResult(stats.errored, "errored"s);
+ appendResult(stats.ignorePassed, "ignored passed"s, unsuccessful);
+ appendResult(stats.ignoreFailed, "ignored"s, true);
+ resultsPage << "</div>\n";
+
+ return resultsPage.str();
+}
+
+} // namespace
+
+void printStats(const TestStats& stats) {
+ const std::size_t count = stats.testCount();
+ if (std::size_t passedTests = stats.passed.size()) {
+ printf(ANSI_COLOR_GREEN "%zu passed (%.1lf%%)" ANSI_COLOR_RESET "\n", passedTests, 100.0 * passedTests / count);
+ }
+ if (std::size_t ignorePassedTests = stats.ignorePassed.size()) {
+ printf(ANSI_COLOR_YELLOW "%zu passed but were ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignorePassedTests, 100.0 * ignorePassedTests / count);
+ }
+ if (std::size_t ignoreFailedTests = stats.ignoreFailed.size()) {
+ printf(ANSI_COLOR_LIGHT_GRAY "%zu ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignoreFailedTests, 100.0 * ignoreFailedTests / count);
+ }
+ if (std::size_t failedTests = stats.failed.size()) {
+ printf(ANSI_COLOR_RED "%zu failed (%.1lf%%)" ANSI_COLOR_RESET "\n", failedTests, 100.0 * failedTests / count);
+ }
+ if (std::size_t erroredTests = stats.errored.size()) {
+ printf(ANSI_COLOR_RED "%zu errored (%.1lf%%)" ANSI_COLOR_RESET "\n", erroredTests, 100.0 * erroredTests / count);
+ }
+}
+
+void writeHTMLResults(const TestStats& stats, const std::string& rootPath, bool shuffle, uint32_t seed) {
+ filesystem::path path = filesystem::path(rootPath) / "index.html"s;
+ try {
+ util::write_file(path.string(), createResultPage(stats, shuffle, seed));
+ printf("Results at: %s\n", path.string().c_str());
+ } catch (std::exception&) {
+ printf(ANSI_COLOR_RED "* ERROR can't write result page %s" ANSI_COLOR_RESET "\n", path.string().c_str());
+ }
+}
diff --git a/expression-test/expression_test_logger.hpp b/expression-test/expression_test_logger.hpp
new file mode 100644
index 0000000000..95b0697cb5
--- /dev/null
+++ b/expression-test/expression_test_logger.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <string>
+
+#define ANSI_COLOR_RED "\x1b[31m"
+#define ANSI_COLOR_GREEN "\x1b[32m"
+#define ANSI_COLOR_YELLOW "\x1b[33m"
+#define ANSI_COLOR_LIGHT_GRAY "\x1b[90m"
+#define ANSI_COLOR_RESET "\x1b[0m"
+
+class TestStats;
+
+void printStats(const TestStats&);
+void writeHTMLResults(const TestStats&, const std::string&, bool, uint32_t);
diff --git a/expression-test/expression_test_parser.cpp b/expression-test/expression_test_parser.cpp
new file mode 100644
index 0000000000..3c194ffee0
--- /dev/null
+++ b/expression-test/expression_test_parser.cpp
@@ -0,0 +1,536 @@
+#include "expression_test_parser.hpp"
+
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <mapbox/geojson/rapidjson.hpp>
+#include <rapidjson/writer.h>
+#include <rapidjson/prettywriter.h>
+#include <rapidjson/stringbuffer.h>
+
+#include <args.hxx>
+
+using namespace mbgl;
+using namespace mbgl::style;
+using namespace mbgl::style::conversion;
+using namespace std::literals;
+
+namespace {
+
+void writeJSON(rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer, const Value& value) {
+ value.match(
+ [&] (const NullValue&) { writer.Null(); },
+ [&] (bool b) { writer.Bool(b); },
+ [&] (uint64_t u) { writer.Uint64(u); },
+ [&] (int64_t i) { writer.Int64(i); },
+ [&] (double d) { d == std::floor(d) ? writer.Int64(d) : writer.Double(d); },
+ [&] (const std::string& s) { writer.String(s); },
+ [&] (const std::vector<Value>& arr) {
+ writer.StartArray();
+ for(const auto& item : arr) {
+ writeJSON(writer, item);
+ }
+ writer.EndArray();
+ },
+ [&] (const std::unordered_map<std::string, Value>& obj) {
+ writer.StartObject();
+ for(const auto& entry : obj) {
+ writer.Key(entry.first.c_str());
+ writeJSON(writer, entry.second);
+ }
+ writer.EndObject();
+ }
+ );
+}
+
+using ErrorMessage = std::string;
+using JSONReply = variant<JSDocument, ErrorMessage>;
+JSONReply readJson(const filesystem::path& jsonPath) {
+ auto maybeJSON = util::readFile(jsonPath);
+ if (!maybeJSON) {
+ return { "Unable to open file "s + jsonPath.string() };
+ }
+
+ JSDocument document;
+ document.Parse<rapidjson::kParseFullPrecisionFlag>(*maybeJSON);
+ if (document.HasParseError()) {
+ return { formatJSONParseError(document) };
+ }
+
+ return { std::move(document) };
+}
+
+std::string toString(const JSValue& value) {
+ assert(value.IsString());
+ return { value.GetString(), value.GetStringLength() };
+}
+
+optional<Value> toValue(const JSValue& jsvalue) {
+ if (jsvalue.IsNull()) {
+ return Value{};
+ }
+
+ if (jsvalue.IsArray()) {
+ std::vector<Value> values;
+ values.reserve(jsvalue.GetArray().Size());
+ for (const auto& v : jsvalue.GetArray()) {
+ if (auto value = toValue(v)) {
+ values.emplace_back(std::move(*value));
+ }
+ }
+ return {std::move(values)};
+ }
+
+ if (jsvalue.IsObject()) {
+ std::unordered_map<std::string, Value> value_map;
+ for (const auto& pair : jsvalue.GetObject()) {
+ if (auto value = toValue(pair.value)) {
+ value_map.emplace(toString(pair.name), std::move(*value));
+ }
+ }
+ return {std::move(value_map)};
+ }
+
+ if (!jsvalue.IsArray() && !jsvalue.IsObject()) {
+ return toValue(Convertible(&jsvalue));
+ }
+
+ return nullopt;
+}
+
+style::expression::type::Type stringToType(const std::string& type) {
+ using namespace style::expression;
+ if (type == "string"s || type == "number-format"s) {
+ return type::String;
+ } else if (type == "number"s) {
+ return type::Number;
+ } else if (type == "boolean"s) {
+ return type::Boolean;
+ } else if (type == "object"s) {
+ return type::Object;
+ } else if (type == "color"s) {
+ return type::Color;
+ } else if (type == "value"s) {
+ return type::Value;
+ } else if (type == "formatted"s) {
+ return type::Formatted;
+ }
+
+ // Should not reach.
+ assert(false);
+ return type::Null;
+}
+
+optional<style::expression::type::Type> toExpressionType(const PropertySpec& spec) {
+ using namespace style::expression;
+ if (spec.type == "array") {
+ type::Type itemType = spec.value.empty() ? type::Value : stringToType(spec.value);
+ if (spec.length) {
+ return {type::Array(itemType, spec.length)};
+ }
+ return {type::Array(itemType)};
+ }
+
+ if (spec.type == "enum") {
+ return {type::String};
+ }
+
+ return spec.type.empty() ? nullopt : optional<type::Type>{stringToType(spec.type)};
+}
+
+void parseCompiled(const JSValue& compiledValue, TestData& data) {
+ const auto& compiled = compiledValue.GetObject();
+ assert(compiled.HasMember("result"));
+ assert(compiled["result"].IsString());
+ const std::string& result = toString(compiled["result"]);
+ data.expected.compiled.success = result == "success";
+
+ if (compiled.HasMember("isFeatureConstant")) {
+ assert(compiled["isFeatureConstant"].IsBool());
+ data.expected.compiled.isFeatureConstant = compiled["isFeatureConstant"].GetBool();
+ }
+
+ if (compiled.HasMember("isZoomConstant")) {
+ assert(compiled["isZoomConstant"].IsBool());
+ data.expected.compiled.isZoomConstant = compiled["isZoomConstant"].GetBool();
+ }
+
+ if (compiled.HasMember("type")) {
+ assert(compiled["type"].IsString());
+ data.expected.compiled.serializedType = toString(compiled["type"]);
+ }
+
+ if (compiled.HasMember("errors")) {
+ assert(compiled["errors"].IsArray());
+ for (const auto& errorVal : compiled["errors"].GetArray()) {
+ assert(errorVal.IsObject());
+ const auto& errorObject = errorVal.GetObject();
+ assert(errorObject.HasMember("key"));
+ assert(errorObject.HasMember("error"));
+
+ std::unordered_map<std::string, Value> errorMap;
+ errorMap.emplace("key"s, Value{toString(errorObject["key"])});
+ errorMap.emplace("error"s, Value{toString(errorObject["error"])});
+ data.expected.compiled.errors.emplace_back(Value{std::move(errorMap)});
+ }
+ }
+}
+
+void parseExpected(const JSValue& expectedValue, TestData& data) {
+ assert(expectedValue.IsObject());
+ const auto& expected = expectedValue.GetObject();
+ assert(expected.HasMember("compiled"));
+ parseCompiled(expected["compiled"], data);
+
+ // set outputs
+ if (expected.HasMember("outputs")) {
+ data.expected.outputs = toValue(expected["outputs"]);
+ }
+
+ // set serialized
+ if (expected.HasMember("serialized")) {
+ data.expected.serialized = toValue(expected["serialized"]);
+ }
+}
+
+void parsePropertySpec(const JSValue& value, TestData& data) {
+ const auto& propertySpec = value.GetObject();
+ PropertySpec spec;
+
+ if (propertySpec.HasMember("type")) {
+ assert(propertySpec["type"].IsString());
+ spec.type = toString(propertySpec["type"]);
+ }
+
+ if (propertySpec.HasMember("value")) {
+ assert(propertySpec["value"].IsString());
+ spec.value = toString(propertySpec["value"]);
+ }
+
+ if (propertySpec.HasMember("length")) {
+ assert(propertySpec["length"].IsNumber());
+ spec.length = propertySpec["length"].GetDouble();
+ }
+
+ if (propertySpec.HasMember("property-type")) {
+ assert(propertySpec["property-type"].IsString());
+ spec.isDataDriven = true;
+ }
+
+ if (propertySpec.HasMember("expression")) {
+ assert(propertySpec["expression"].IsObject());
+ spec.expression = toValue(propertySpec["expression"]);
+ }
+
+ data.spec = std::move(spec);
+}
+
+bool parseInputs(const JSValue& inputsValue, TestData& data) {
+ assert(inputsValue.IsArray());
+ for (const auto& input : inputsValue.GetArray()) {
+ assert(input.IsArray());
+ assert(input.Size() == 2);
+ assert(input[0].IsObject());
+ assert(input[1].IsObject());
+
+ // Parse evaluation context, zoom.
+ optional<float> zoom;
+ const auto& evaluationContext = input[0].GetObject();
+ if (evaluationContext.HasMember("zoom")) {
+ assert(evaluationContext["zoom"].IsNumber());
+ zoom = evaluationContext["zoom"].GetDouble();
+ }
+
+ // Parse heatmap density
+ optional<double> heatmapDensity;
+ if (evaluationContext.HasMember("heatmapDensity")) {
+ assert(evaluationContext["heatmapDensity"].IsNumber());
+ heatmapDensity = evaluationContext["heatmapDensity"].GetDouble();
+ }
+
+ // Parse feature properties
+ Feature feature(mapbox::geometry::point<double>(0.0, 0.0));
+ const auto& featureObject = input[1].GetObject();
+ if (featureObject.HasMember("properties")) {
+ assert(featureObject["properties"].IsObject());
+ feature.properties = mapbox::geojson::convert<PropertyMap>(featureObject["properties"]);
+ }
+
+ if (featureObject.HasMember("geometry")) {
+ assert(featureObject["geometry"].IsObject());
+ feature.geometry = mapbox::geojson::convert<mapbox::geometry::geometry<double>>(featureObject["geometry"]);
+ }
+
+ if (featureObject.HasMember("id")) {
+ assert(featureObject["id"].IsNumber() || featureObject["id"].IsString());
+ feature.id = mapbox::geojson::convert<mapbox::feature::identifier>(featureObject["id"]);
+ }
+
+ data.inputs.emplace_back(std::move(zoom), std::move(heatmapDensity), std::move(feature));
+ }
+ return true;
+}
+
+} // namespace
+
+std::tuple<filesystem::path, std::vector<filesystem::path>, bool, uint32_t> parseArguments(int argc, char** argv) {
+ args::ArgumentParser argumentParser("Mapbox GL Expression Test Runner");
+
+ args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", { 'h', "help" });
+ args::Flag shuffleFlag(argumentParser, "shuffle", "Toggle shuffling the tests order",
+ { 's', "shuffle" });
+ args::ValueFlag<uint32_t> seedValue(argumentParser, "seed", "Shuffle seed (default: random)",
+ { "seed" });
+ args::PositionalList<std::string> testNameValues(argumentParser, "URL", "Test name(s)");
+
+ try {
+ argumentParser.ParseCLI(argc, argv);
+ } catch (const args::Help&) {
+ std::ostringstream stream;
+ stream << argumentParser;
+ Log::Info(Event::General, stream.str());
+ exit(0);
+ } catch (const args::ParseError& e) {
+ std::ostringstream stream;
+ stream << argumentParser;
+ Log::Info(Event::General, stream.str());
+ Log::Error(Event::General, e.what());
+ exit(1);
+ } catch (const args::ValidationError& e) {
+ std::ostringstream stream;
+ stream << argumentParser;
+ Log::Info(Event::General, stream.str());
+ Log::Error(Event::General, e.what());
+ exit(2);
+ }
+
+ filesystem::path rootPath {std::string(TEST_RUNNER_ROOT_PATH).append("/mapbox-gl-js/test/integration/expression-tests")};
+ if (!filesystem::exists(rootPath)) {
+ Log::Error(Event::General, "Test path '%s' does not exist.", rootPath.string().c_str());
+ exit(3);
+ }
+
+ std::vector<filesystem::path> paths;
+ for (const auto& testName : args::get(testNameValues)) {
+ paths.emplace_back(rootPath.string() + "/" + testName);
+ }
+
+ if (paths.empty()) {
+ paths.emplace_back(rootPath);
+ }
+
+ // Recursively traverse through the test paths and collect test directories containing "test.json".
+ std::vector<filesystem::path> testPaths;
+ testPaths.reserve(paths.size());
+ for (const auto& path : paths) {
+ if (!filesystem::exists(path)) {
+ Log::Warning(Event::General, "Provided test folder '%s' does not exist.", path.string().c_str());
+ continue;
+ }
+
+ for (auto& testPath : filesystem::recursive_directory_iterator(path)) {
+ if (testPath.path().filename() == "test.json") {
+ testPaths.emplace_back(testPath.path());
+ }
+ }
+ }
+
+ return Arguments{ std::move(rootPath),
+ std::move(testPaths),
+ shuffleFlag ? args::get(shuffleFlag) : false,
+ seedValue ? args::get(seedValue) : 1u };
+}
+
+Ignores parseExpressionIgnores() {
+ Ignores ignores;
+ const auto mainIgnoresPath = filesystem::path(TEST_RUNNER_ROOT_PATH).append("platform/node/test/ignores.json");
+ auto maybeIgnores = readJson(mainIgnoresPath);
+ if (!maybeIgnores.is<JSDocument>()) { // NOLINT
+ return {};
+ }
+
+ for (const auto& property : maybeIgnores.get<JSDocument>().GetObject()) {
+ std::string id{ toString(property.name) };
+ // Keep only expression-test ignores
+ if (id.rfind("expression-tests", 0) != 0) {
+ continue;
+ }
+ std::string reason{ toString(property.value) };
+ ignores.emplace_back(std::move(id), std::move(reason));
+ }
+
+ return ignores;
+}
+
+optional<TestData> parseTestData(const filesystem::path& path) {
+ TestData data;
+ auto maybeJson = readJson(path.string());
+ if (!maybeJson.is<JSDocument>()) { // NOLINT
+ Log::Error(Event::General, "Cannot parse test '%s'.", path.string().c_str());
+ return nullopt;
+ }
+
+ data.document = std::move(maybeJson.get<JSDocument>());
+
+ // Check that mandatory test data members are present.
+ if (!data.document.HasMember("expression") || !data.document.HasMember("expected")) {
+ Log::Error(Event::General, "Test fixture '%s' does not contain required data.", path.string().c_str());
+ return nullopt;
+ }
+
+ // Parse propertySpec
+ if (data.document.HasMember("propertySpec")) {
+ assert(data.document["propertySpec"].IsObject());
+ parsePropertySpec(data.document["propertySpec"], data);
+ }
+
+ // Parse expected
+ parseExpected(data.document["expected"], data);
+
+ // Parse inputs
+ if (data.document.HasMember("inputs") && !parseInputs(data.document["inputs"], data)) {
+ Log::Error(Event::General,"Can't convert inputs value for '%s'", path.string().c_str());
+ return nullopt;
+ }
+
+ return {std::move(data)};
+}
+
+std::string toJSON(const Value& value, unsigned indent, bool singleLine) {
+ rapidjson::StringBuffer buffer;
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
+ if (singleLine) {
+ writer.SetFormatOptions(rapidjson::kFormatSingleLineArray);
+ }
+ writer.SetIndent(' ', indent);
+ writeJSON(writer, value);
+ return buffer.GetString();
+}
+
+JSDocument toDocument(const Value& value) {
+ JSDocument document;
+ document.Parse<rapidjson::kParseFullPrecisionFlag>(toJSON(value));
+ return document;
+}
+
+Value toValue(const Compiled& compiled) {
+ std::unordered_map<std::string, Value> compiledObject;
+ compiledObject.emplace("result", compiled.success ? "success"s : "error"s);
+ if (compiled.success) {
+ compiledObject.emplace("isFeatureConstant", compiled.isFeatureConstant);
+ compiledObject.emplace("isZoomConstant", compiled.isZoomConstant);
+ compiledObject.emplace("type", compiled.serializedType);
+ } else {
+ compiledObject.emplace("errors", compiled.errors);
+ }
+ return {std::move(compiledObject)};
+}
+
+// Serializes native expression types to JS format.
+// Color and Formatted are exceptions.
+// TODO: harmonize serialized format to remove this conversion.
+optional<Value> toValue(const expression::Value& exprValue) {
+ return exprValue.match(
+ [](const Color& c) -> optional<Value> {
+ std::vector<Value> color { double(c.r), double(c.g), double(c.b), double(c.a) };
+ return {Value{std::move(color)}};
+ },
+ [](const expression::Formatted& formatted) -> optional<Value> {
+ std::unordered_map<std::string, Value> serialized;
+ std::vector<Value> sections;
+ for (const auto& section : formatted.sections) {
+ std::unordered_map<std::string, Value> serializedSection;
+ serializedSection.emplace("text", section.text);
+ if (section.fontScale) {
+ serializedSection.emplace("scale", *section.fontScale);
+ } else {
+ serializedSection.emplace("scale", NullValue());
+ }
+ if (section.fontStack) {
+ std::string fontStackString;
+ serializedSection.emplace("fontStack", fontStackToString(*section.fontStack));
+ } else {
+ serializedSection.emplace("fontStack", NullValue());
+ }
+ if (section.textColor) {
+ serializedSection.emplace("textColor", section.textColor->toObject());
+ } else {
+ serializedSection.emplace("textColor", NullValue());
+ }
+ sections.emplace_back(serializedSection);
+ }
+ serialized.emplace("sections", sections);
+ return {Value{std::move(serialized)}};
+ },
+ [](const std::vector<expression::Value>& values) -> optional<Value> {
+ std::vector<Value> mbglValues;
+ for (const auto& value : values) {
+ if (auto converted = expression::fromExpressionValue<Value>(value)) {
+ mbglValues.emplace_back(std::move(*converted));
+ }
+ }
+ return {Value{std::move(mbglValues)}};
+ },
+ [](const std::unordered_map<std::string, expression::Value>& valueMap) -> optional<Value> {
+ std::unordered_map<std::string, Value> mbglValueMap;
+ for (const auto& pair : valueMap) {
+ if (auto converted = expression::fromExpressionValue<Value>(pair.second)) {
+ mbglValueMap.emplace(pair.first, std::move(*converted));
+ }
+ }
+ return {Value{std::move(mbglValueMap)}};
+ },
+ [](const auto& v) { return expression::fromExpressionValue<Value>(v); });
+}
+
+std::unique_ptr<style::expression::Expression> parseExpression(const JSValue& value,
+ optional<PropertySpec>& spec,
+ TestResult& result) {
+ optional<style::expression::type::Type> expected = spec ? toExpressionType(*spec) : nullopt;
+ expression::ParsingContext ctx = expected ? expression::ParsingContext(*expected) :
+ expression::ParsingContext();
+ Convertible convertible(&value);
+ expression::ParseResult parsed;
+ if (value.IsObject() && !value.IsArray() && expected){
+ Error error;
+ parsed = convertFunctionToExpression(*expected, convertible, error, false /*convert tokens*/);
+ if (!parsed) {
+ // TODO: should the error message be checked for function conversion?
+ }
+ } else {
+ parsed = ctx.parseLayerPropertyExpression(convertible);
+ if (!parsed) {
+ for (const auto& parsingError : ctx.getErrors()) {
+ std::unordered_map<std::string, Value> errorMap;
+ errorMap.emplace("key"s, Value{parsingError.key});
+ errorMap.emplace("error"s, Value{parsingError.message});
+ result.compiled.errors.emplace_back(Value{std::move(errorMap)});
+ }
+ }
+ }
+
+ result.expression = toValue(value);
+ result.compiled.success = bool(parsed);
+ if (parsed) {
+ result.compiled.isFeatureConstant = expression::isFeatureConstant(**parsed);
+ result.compiled.isZoomConstant = expression::isZoomConstant(**parsed);
+ result.compiled.serializedType = style::expression::type::toString((*parsed)->getType());
+ result.serialized = (*parsed)->serialize();
+ return std::move(*parsed);
+ }
+
+ return nullptr;
+}
+
+std::unique_ptr<style::expression::Expression> parseExpression(const optional<Value>& value,
+ optional<PropertySpec>& spec,
+ TestResult& result) {
+ assert(value);
+ auto document = toDocument(*value);
+ assert(!document.HasParseError());
+ return parseExpression(document, spec, result);
+}
diff --git a/expression-test/expression_test_parser.hpp b/expression-test/expression_test_parser.hpp
new file mode 100644
index 0000000000..561ccd9647
--- /dev/null
+++ b/expression-test/expression_test_parser.hpp
@@ -0,0 +1,94 @@
+#pragma once
+
+#include "filesystem.hpp"
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/rapidjson.hpp>
+
+#include <vector>
+#include <string>
+
+using namespace mbgl;
+
+struct Input {
+ Input(optional<float> zoom_, optional<double> heatmapDensity_, Feature feature_)
+ : zoom(std::move(zoom_)),
+ heatmapDensity(std::move(heatmapDensity_)),
+ feature(std::move(feature_)) {}
+ optional<float> zoom;
+ optional<double> heatmapDensity;
+ Feature feature;
+};
+
+struct Compiled {
+ bool operator==(const Compiled& other) const {
+ bool typeEqual = success == other.success &&
+ isFeatureConstant == other.isFeatureConstant &&
+ isZoomConstant == other.isZoomConstant &&
+ serializedType == other.serializedType &&
+ errors == other.errors;
+ return typeEqual;
+ }
+
+ bool success = false;
+ bool isFeatureConstant = false;
+ bool isZoomConstant = false;
+ std::string serializedType;
+ std::vector<Value> errors;
+};
+
+struct TestResult {
+ Compiled compiled;
+ optional<Value> expression;
+ optional<Value> outputs;
+ optional<Value> serialized;
+};
+
+struct PropertySpec {
+ std::string type;
+ std::string value;
+ std::size_t length = 0;
+ bool isDataDriven = false;
+ optional<Value> expression;
+};
+
+class TestData {
+public:
+ std::vector<Input> inputs;
+ TestResult expected;
+ TestResult result;
+ TestResult recompiled;
+ optional<PropertySpec> spec;
+ JSDocument document;
+};
+
+struct Ignore {
+ Ignore(std::string id_, std::string reason_)
+ : id(std::move(id_)),
+ reason(std::move(reason_)) {}
+
+ std::string id;
+ std::string reason;
+};
+
+using Arguments = std::tuple<filesystem::path, std::vector<filesystem::path>, bool, uint32_t>;
+Arguments parseArguments(int argc, char** argv);
+
+using Ignores = std::vector<Ignore>;
+Ignores parseExpressionIgnores();
+optional<TestData> parseTestData(const filesystem::path&);
+
+std::string toJSON(const Value& value, unsigned indent = 0, bool singleLine = false);
+JSDocument toDocument(const Value&);
+Value toValue(const Compiled&);
+optional<Value> toValue(const style::expression::Value&);
+
+std::unique_ptr<style::expression::Expression> parseExpression(const JSValue&,
+ optional<PropertySpec>&,
+ TestResult&);
+std::unique_ptr<style::expression::Expression> parseExpression(const optional<Value>&,
+ optional<PropertySpec>&,
+ TestResult&);
+
diff --git a/expression-test/expression_test_runner.cpp b/expression-test/expression_test_runner.cpp
new file mode 100644
index 0000000000..695d129049
--- /dev/null
+++ b/expression-test/expression_test_runner.cpp
@@ -0,0 +1,303 @@
+#include "expression_test_runner.hpp"
+#include "expression_test_parser.hpp"
+#include "expression_test_logger.hpp"
+#include "filesystem.hpp"
+
+#include <mbgl/util/io.hpp>
+
+#include <rapidjson/writer.h>
+#include <rapidjson/prettywriter.h>
+#include <rapidjson/stringbuffer.h>
+
+#include <sstream>
+#include <regex>
+
+using namespace std::literals;
+
+namespace {
+
+// Strip precision for numbers, so that we can compare evaluated results with fixtures.
+// Copied from JS expression harness.
+Value stripPrecision(const Value& value) {
+ const double decimalSigFigs = 6;
+ if (auto num = numericValue<double>(value)) {
+ if (*num == 0) {
+ return *num;
+ }
+
+ const double multiplier = std::pow(10,
+ std::max(0.0, decimalSigFigs - std::ceil(std::log10(std::fabs(*num)))));
+
+ // We strip precision twice in a row here to avoid cases where
+ // stripping an already stripped number will modify its value
+ // due to bad floating point precision luck
+ // eg `Math.floor(8.16598 * 100000) / 100000` -> 8.16597
+ const double firstStrip = std::floor(*num * multiplier) / multiplier;
+ return std::floor(firstStrip * multiplier) / multiplier;
+ }
+
+ if (value.is<std::vector<Value>>()) {
+ std::vector<Value> stripped;
+ const auto& vec = value.get<std::vector<Value>>();
+ stripped.reserve(vec.size());
+ for (const auto& val : vec) {
+ stripped.emplace_back(stripPrecision(val));
+ }
+ return stripped;
+ } else if (value.is<std::unordered_map<std::string, Value>>()) {
+ std::unordered_map<std::string, Value> stripped;
+ const auto& map = value.get<std::unordered_map<std::string, Value>>();
+ for (const auto& pair : map) {
+ stripped.emplace(pair.first, stripPrecision(pair.second));
+ }
+ return stripped;
+ }
+
+ return value;
+}
+
+bool deepEqual(const Value& a, const Value& b) {
+ const auto& anum = numericValue<double>(a);
+ const auto& bnum = numericValue<double>(b);
+ if (anum && bnum) {
+ return stripPrecision(*anum) == stripPrecision(*bnum);
+ }
+
+ if (a.which() != b.which()) {
+ return false;
+ }
+
+ if (a.is<std::vector<Value>>()) {
+ const auto& avec = a.get<std::vector<Value>>();
+ const auto& bvec = b.get<std::vector<Value>>();
+ if (avec.size() != bvec.size()) {
+ return false;
+ }
+ for (std::size_t i = 0; i < avec.size(); ++i) {
+ if (!deepEqual(avec[i], bvec[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ if (a.is<std::unordered_map<std::string, Value>>()) {
+ const auto& amap = a.get<std::unordered_map<std::string, Value>>();
+ const auto& bmap = b.get<std::unordered_map<std::string, Value>>();
+ if (amap.size() != bmap.size()) {
+ return false;
+ }
+ for (const auto& pair : amap) {
+ auto it = bmap.find(pair.first);
+ if (it == bmap.end()) {
+ return false;
+ }
+ if (!deepEqual(pair.second, it->second)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return a == b;
+}
+
+bool deepEqual(const optional<Value>& a, const optional<Value>& b) {
+ if ((a && !b) || (!a && b)) {
+ return false;
+ }
+
+ if (a && b) {
+ return deepEqual(*a, *b);
+ }
+
+ return true;
+}
+
+std::vector<std::string> tokenize(std::string str) {
+ std::vector<std::string> tokens;
+ std::regex re("\n");
+ std::copy(std::regex_token_iterator<std::string::iterator>(str.begin(), str.end(), re, -1),
+ std::regex_token_iterator<std::string::iterator>(),
+ std::back_inserter(tokens));
+ return tokens;
+}
+
+std::string simpleDiff(const Value& result, const Value& expected) {
+ std::vector<std::string> resultTokens {tokenize(toJSON(result, 2, true))};
+ std::vector<std::string> expectedTokens {tokenize(toJSON(expected, 2, true))};
+ std::size_t maxLength = std::max(resultTokens.size(), expectedTokens.size());
+ std::ostringstream diff;
+ const auto flush = [] (const std::vector<std::string>& vec, std::size_t pos, std::ostringstream& out, std::string separator) {
+ for (std::size_t j = pos; j < vec.size(); ++j) {
+ out << separator << vec[j] << std::endl;
+ }
+ };
+
+ for (std::size_t i = 0; i < maxLength; ++i) {
+ if (resultTokens.size() <= i) {
+ flush(expectedTokens, i, diff, "+"s);
+ break;
+ }
+
+ if (expectedTokens.size() <= i) {
+ flush(expectedTokens, i, diff, "-"s);
+ break;
+ }
+
+ if (resultTokens[i] != expectedTokens[i]) {
+ diff << "-"s << expectedTokens[i] << std::endl;
+ diff << "+"s << resultTokens[i] << std::endl;
+ } else {
+ diff << resultTokens[i] << std::endl;
+ }
+ }
+ return diff.str();
+}
+
+void rewriteRoundtrippedType(const std::string& expected, std::string& actual) {
+ if (!expected.rfind("array", 0) && !actual.rfind("array", 0)) {
+ actual = expected;
+ }
+}
+
+void updateTest(TestData& data) {
+ assert(data.document["expected"].IsObject());
+ auto& expected = data.document["expected"];
+ auto compiled = toDocument(toValue(data.result.compiled));
+ assert(!compiled.HasParseError());
+ expected["compiled"].Swap(compiled);
+
+ if (data.result.outputs) {
+ auto outputs = toDocument(stripPrecision(data.result.outputs.value_or(Value{})));
+ assert(!outputs.HasParseError());
+ expected["outputs"].Swap(outputs);
+ } else {
+ expected.RemoveMember("outputs");
+ }
+
+ if (data.result.serialized) {
+ auto serialized = toDocument(*data.result.serialized);
+ assert(!serialized.HasParseError());
+ expected["serialized"].Swap(serialized);
+ } else {
+ expected.RemoveMember("serialized");
+ }
+}
+
+void writeTestData(const JSDocument& document, const std::string& rootPath, const std::string& id) {
+ rapidjson::StringBuffer buffer;
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
+ writer.SetFormatOptions(rapidjson::kFormatSingleLineArray);
+ writer.SetIndent(' ', 2);
+ document.Accept(writer);
+ buffer.Put('\n');
+ filesystem::path path = filesystem::path(rootPath) / id / "test.json"s;
+ try {
+ util::write_file(path.string(), {buffer.GetString(), buffer.GetSize()});
+ } catch (std::exception&) {
+ printf(ANSI_COLOR_RED "* ERROR can't update '%s' test" ANSI_COLOR_RESET "\n", id.c_str());
+ }
+}
+
+} // namespace
+
+TestRunOutput runExpressionTest(TestData& data, const std::string& rootPath, const std::string& id) {
+ TestRunOutput output(id);
+ const auto evaluateExpression = [&data](std::unique_ptr<style::expression::Expression>& expression,
+ TestResult& result) {
+ assert(expression);
+ std::vector<Value> outputs;
+ if (!data.inputs.empty()) {
+ for (const auto& input : data.inputs) {
+ auto evaluationResult = expression->evaluate(input.zoom, input.feature, input.heatmapDensity);
+ if (!evaluationResult) {
+ std::unordered_map<std::string, Value> error{{"error", Value{evaluationResult.error().message}}};
+ outputs.emplace_back(Value{std::move(error)});
+ } else {
+ auto value = toValue(*evaluationResult);
+ assert(value);
+ outputs.emplace_back(Value{*value});
+ }
+ }
+ }
+ result.outputs = {Value{std::move(outputs)}};
+ };
+
+ // Parse expression
+ auto parsedExpression = parseExpression(data.document["expression"], data.spec, data.result);
+ output.expression = toJSON(data.result.expression.value_or(Value{}), 2, true);
+
+ // Evaluate expression
+ if (parsedExpression) {
+ evaluateExpression(parsedExpression, data.result);
+ output.serialized = toJSON(data.result.serialized.value_or(Value{}), 2, true);
+
+ // round trip
+ auto recompiledExpression = parseExpression(data.result.serialized, data.spec, data.recompiled);
+ if (recompiledExpression) {
+ evaluateExpression(recompiledExpression, data.recompiled);
+ rewriteRoundtrippedType(data.expected.compiled.serializedType,
+ data.recompiled.compiled.serializedType);
+ }
+ }
+
+ if (getenv("UPDATE")) {
+ output.passed = true;
+ updateTest(data);
+ writeTestData(data.document, rootPath, id);
+ return output;
+ }
+
+ bool compileOk = data.result.compiled == data.expected.compiled;
+ bool evalOk = compileOk && deepEqual(data.result.outputs, data.expected.outputs);
+
+ bool recompileOk = true;
+ bool roundTripOk = true;
+ bool serializationOk = true;
+
+ if (data.expected.compiled.success) {
+ serializationOk = compileOk && deepEqual(data.result.serialized, data.expected.serialized);
+ recompileOk = compileOk && data.recompiled.compiled == data.expected.compiled;
+ roundTripOk = recompileOk && deepEqual(data.recompiled.outputs, data.expected.outputs);
+ }
+
+ output.passed = compileOk && evalOk && recompileOk && roundTripOk && serializationOk;
+
+ if (!compileOk) {
+ auto resultValue = toValue(data.result.compiled);
+ auto expectedValue = toValue(data.expected.compiled);
+ output.text += "Compiled expression difference:\n"s +
+ simpleDiff(resultValue, expectedValue) +
+ "\n"s;
+ }
+
+ if (compileOk && !serializationOk) {
+ auto diff = simpleDiff(data.expected.serialized.value_or(Value{}),
+ data.result.serialized.value_or(Value{}));
+ output.text += "Serialized expression difference:\n"s + diff + "\n"s;
+ }
+
+ if (compileOk && !recompileOk) {
+ auto recompiledValue = toValue(data.recompiled.compiled);
+ auto expectedValue = toValue(data.expected.compiled);
+ output.text += "Recompiled expression difference:\n"s +
+ simpleDiff(recompiledValue, expectedValue) +
+ "\n"s;
+ }
+
+ if (compileOk && !evalOk) {
+ auto diff = simpleDiff(data.expected.outputs.value_or(Value{}),
+ data.result.outputs.value_or(Value{}));
+ output.text += "Expression outputs difference:\n"s + diff + "\n"s;
+ }
+
+ if (recompileOk && !roundTripOk) {
+ auto diff = simpleDiff(data.expected.outputs.value_or(Value{}),
+ data.recompiled.outputs.value_or(Value{}));
+ output.text += "Roundtripped through serialize expression outputs difference:\n"s +
+ diff + "\n"s;
+ }
+
+ return output;
+}
diff --git a/expression-test/expression_test_runner.hpp b/expression-test/expression_test_runner.hpp
new file mode 100644
index 0000000000..596d5c11b6
--- /dev/null
+++ b/expression-test/expression_test_runner.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <vector>
+#include <string>
+
+class TestData;
+
+struct TestRunOutput {
+ TestRunOutput(std::string id_) : id(std::move(id_)) {}
+ std::string id;
+ bool passed = false;
+ std::string text;
+ std::string expression;
+ std::string serialized;
+};
+
+class TestStats {
+public:
+ std::size_t testCount() const {
+ return passed.size() + failed.size() + errored.size() +
+ ignorePassed.size() + ignoreFailed.size();
+ }
+
+ std::vector<TestRunOutput> passed;
+ std::vector<TestRunOutput> failed;
+ std::vector<TestRunOutput> errored;
+ std::vector<TestRunOutput> ignorePassed;
+ std::vector<TestRunOutput> ignoreFailed;
+ std::vector<std::string> ids;
+};
+
+TestRunOutput runExpressionTest(TestData&, const std::string& rootPath, const std::string& id);
diff --git a/expression-test/filesystem.hpp b/expression-test/filesystem.hpp
new file mode 100644
index 0000000000..cee7e9d911
--- /dev/null
+++ b/expression-test/filesystem.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <ghc/filesystem.hpp>
+
+namespace mbgl {
+
+namespace filesystem = ghc::filesystem;
+
+} // namespace mbgl
diff --git a/expression-test/main.cpp b/expression-test/main.cpp
new file mode 100644
index 0000000000..db468dffe0
--- /dev/null
+++ b/expression-test/main.cpp
@@ -0,0 +1,75 @@
+#include "expression_test_parser.hpp"
+#include "expression_test_runner.hpp"
+#include "expression_test_logger.hpp"
+
+#include <random>
+
+int main(int argc, char** argv) {
+ // Parse args
+ std::vector<mbgl::filesystem::path> testPaths;
+ mbgl::filesystem::path rootPath;
+ bool shuffle;
+ uint32_t seed;
+ std::tie(rootPath, testPaths, shuffle, seed) = parseArguments(argc, argv);
+
+ // Parse ignores
+ const auto ignores = parseExpressionIgnores();
+
+ if (shuffle) {
+ printf(ANSI_COLOR_YELLOW "Shuffle seed: %d" ANSI_COLOR_RESET "\n", seed);
+ std::seed_seq sequence { seed };
+ std::mt19937 shuffler(sequence);
+ std::shuffle(testPaths.begin(), testPaths.end(), shuffler);
+ }
+
+ // Run tests, collect stats and output of each run.
+ TestStats stats;
+ for (const auto& path : testPaths) {
+ const auto& expectation = path.parent_path().string();
+ std::string id = expectation.substr(rootPath.string().length() + 1, expectation.length() - rootPath.string().length());
+ stats.ids.emplace_back(id);
+
+ bool shouldIgnore = false;
+ std::string ignoreReason;
+ const std::string ignoreName = "expression-tests/" + id;
+ const auto it = std::find_if(ignores.cbegin(), ignores.cend(), [&ignoreName] (const auto& ignore) { return ignore.id == ignoreName; });
+ if (it != ignores.end()) {
+ shouldIgnore = true;
+ ignoreReason = (*it).reason;
+ }
+
+ optional<TestRunOutput> testRun;
+ if (auto testData = parseTestData(path)) {
+ testRun = runExpressionTest(*testData, rootPath.string(), id);
+ }
+
+ if (!testRun) {
+ stats.errored.emplace_back(id);
+ printf(ANSI_COLOR_RED "* ERROR can't parse '%s' test" ANSI_COLOR_RESET "\n", id.c_str());
+ continue;
+ }
+
+ if (shouldIgnore) {
+ if (testRun->passed) {
+ stats.ignorePassed.emplace_back(std::move(*testRun));
+ printf(ANSI_COLOR_YELLOW "* PASSED ignored test %s (%s)" ANSI_COLOR_RESET "\n", id.c_str(), ignoreReason.c_str());
+ } else {
+ stats.ignoreFailed.emplace_back(std::move(*testRun));
+ printf(ANSI_COLOR_LIGHT_GRAY "* FAILED ignored test %s (%s)" ANSI_COLOR_RESET "\n", id.c_str(), ignoreReason.c_str());
+ }
+ } else {
+ if (testRun->passed) {
+ stats.passed.emplace_back(std::move(*testRun));
+ printf(ANSI_COLOR_GREEN "* PASSED %s" ANSI_COLOR_RESET "\n", id.c_str());
+ } else {
+ printf(ANSI_COLOR_RED "* FAILED %s\n%s" ANSI_COLOR_RESET, id.c_str(), testRun->text.c_str());
+ stats.failed.emplace_back(std::move(*testRun));
+ }
+ }
+ }
+
+ printStats(stats);
+ writeHTMLResults(stats, rootPath.string(), shuffle, seed);
+
+ return stats.errored.size() + stats.failed.size() == 0 ? 0 : 1;
+}
diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp
index fb9ea5eba2..17728741bb 100644
--- a/include/mbgl/annotation/annotation.hpp
+++ b/include/mbgl/annotation/annotation.hpp
@@ -4,7 +4,6 @@
#include <mbgl/util/variant.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/style/property_value.hpp>
-#include <mbgl/style/property_value.hpp>
#include <cstdint>
#include <vector>
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index b1d4b14e4f..ac0a398d25 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -60,7 +60,7 @@ public:
bool isPanning() const;
// Camera
- CameraOptions getCameraOptions(const EdgeInsets& = {}) const;
+ CameraOptions getCameraOptions(optional<EdgeInsets> = {}) const;
void jumpTo(const CameraOptions&);
void easeTo(const CameraOptions&, const AnimationOptions&);
void flyTo(const CameraOptions&, const AnimationOptions&);
diff --git a/include/mbgl/map/map_observer.hpp b/include/mbgl/map/map_observer.hpp
index a79f5ac82d..258699a2a6 100644
--- a/include/mbgl/map/map_observer.hpp
+++ b/include/mbgl/map/map_observer.hpp
@@ -34,6 +34,12 @@ public:
Full
};
+ struct RenderFrameStatus {
+ RenderMode mode;
+ bool needsRepaint; // In continous mode, shows that there are ongoig transitions.
+ bool placementChanged;
+ };
+
virtual void onCameraWillChange(CameraChangeMode) {}
virtual void onCameraIsChanging() {}
virtual void onCameraDidChange(CameraChangeMode) {}
@@ -41,7 +47,7 @@ public:
virtual void onDidFinishLoadingMap() {}
virtual void onDidFailLoadingMap(MapLoadError, const std::string&) {}
virtual void onWillStartRenderingFrame() {}
- virtual void onDidFinishRenderingFrame(RenderMode) {}
+ virtual void onDidFinishRenderingFrame(RenderFrameStatus) {}
virtual void onWillStartRenderingMap() {}
virtual void onDidFinishRenderingMap(RenderMode) {}
virtual void onDidFinishLoadingStyle() {}
diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp
index 787f9dceee..f1800dbfb8 100644
--- a/include/mbgl/renderer/renderer.hpp
+++ b/include/mbgl/renderer/renderer.hpp
@@ -24,7 +24,6 @@ class RendererBackend;
class Renderer {
public:
Renderer(gfx::RendererBackend&, float pixelRatio_,
- const optional<std::string> programCacheDir = {},
const optional<std::string> localFontFamily = {});
~Renderer();
diff --git a/include/mbgl/renderer/renderer_observer.hpp b/include/mbgl/renderer/renderer_observer.hpp
index 4d21a0aaee..e0fc84215e 100644
--- a/include/mbgl/renderer/renderer_observer.hpp
+++ b/include/mbgl/renderer/renderer_observer.hpp
@@ -26,8 +26,8 @@ public:
// 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) {}
+ // End of frame, booleans flags that a repaint is required and that placement changed.
+ virtual void onDidFinishRenderingFrame(RenderMode, bool /*repaint*/, bool /*placementChanged*/) {}
// Final frame
virtual void onDidFinishRenderingMap() {}
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index f702f47edc..baf43dc5f5 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -129,6 +129,11 @@ public:
uint64_t completedTileCount = 0;
/**
+ * The number of tiles that are known to be required for this region.
+ */
+ uint64_t requiredTileCount = 0;
+
+ /**
* The cumulative size, in bytes, of all tiles that have been fully downloaded.
* This is a subset of `completedResourceSize`.
*/
diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp
index 40c4c836b1..d00f336669 100644
--- a/include/mbgl/storage/resource.hpp
+++ b/include/mbgl/storage/resource.hpp
@@ -1,11 +1,10 @@
#pragma once
#include <mbgl/storage/response.hpp>
+#include <mbgl/util/bitmask_operations.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/tileset.hpp>
-#include <mbgl/util/util.hpp>
-#include <mbgl/util/traits.hpp>
#include <string>
@@ -98,21 +97,8 @@ public:
std::shared_ptr<const std::string> priorData;
};
-
-MBGL_CONSTEXPR Resource::LoadingMethod operator|(Resource::LoadingMethod a, Resource::LoadingMethod b) {
- return Resource::LoadingMethod(mbgl::underlying_type(a) | mbgl::underlying_type(b));
-}
-
-MBGL_CONSTEXPR Resource::LoadingMethod& operator|=(Resource::LoadingMethod& a, Resource::LoadingMethod b) {
- return (a = a | b);
-}
-
-MBGL_CONSTEXPR Resource::LoadingMethod operator&(Resource::LoadingMethod a, Resource::LoadingMethod b) {
- return Resource::LoadingMethod(mbgl::underlying_type(a) & mbgl::underlying_type(b));
-}
-
inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) {
- return (loadingMethod & method) != Resource::LoadingMethod::None;
+ return (loadingMethod & method);
}
} // namespace mbgl
diff --git a/include/mbgl/style/expression/dsl.hpp b/include/mbgl/style/expression/dsl.hpp
index bcab999ab2..4abeac7989 100644
--- a/include/mbgl/style/expression/dsl.hpp
+++ b/include/mbgl/style/expression/dsl.hpp
@@ -1,13 +1,13 @@
#pragma once
-#include <mbgl/style/expression/value.hpp>
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/expression/interpolator.hpp>
+#include <mbgl/style/expression/value.hpp>
#include <mbgl/util/ignore.hpp>
+#include <initializer_list>
#include <memory>
#include <string>
-#include <initializer_list>
namespace mbgl {
namespace style {
@@ -24,15 +24,18 @@ std::vector<std::unique_ptr<Expression>> vec(Args... args) {
return result;
}
+std::unique_ptr<Expression> createExpression(const char* expr);
+std::unique_ptr<Expression> createExpression(const mbgl::style::conversion::Convertible& expr);
std::unique_ptr<Expression> error(std::string);
std::unique_ptr<Expression> literal(const char* value);
std::unique_ptr<Expression> literal(Value value);
std::unique_ptr<Expression> literal(std::initializer_list<double> value);
-std::unique_ptr<Expression> literal(std::initializer_list<const char *> value);
+std::unique_ptr<Expression> literal(std::initializer_list<const char*> value);
-std::unique_ptr<Expression> assertion(type::Type, std::unique_ptr<Expression>,
- std::unique_ptr<Expression> def = nullptr);
+std::unique_ptr<Expression>
+assertion(type::Type, std::unique_ptr<Expression>,
+ std::unique_ptr<Expression> def = nullptr);
std::unique_ptr<Expression> number(std::unique_ptr<Expression>,
std::unique_ptr<Expression> def = nullptr);
std::unique_ptr<Expression> string(std::unique_ptr<Expression>,
@@ -46,21 +49,17 @@ std::unique_ptr<Expression> toString(std::unique_ptr<Expression>,
std::unique_ptr<Expression> def = nullptr);
std::unique_ptr<Expression> toFormatted(std::unique_ptr<Expression>,
std::unique_ptr<Expression> def = nullptr);
-
+
std::unique_ptr<Expression> get(const char* value);
std::unique_ptr<Expression> get(std::unique_ptr<Expression>);
std::unique_ptr<Expression> id();
std::unique_ptr<Expression> zoom();
-std::unique_ptr<Expression> eq(std::unique_ptr<Expression>,
- std::unique_ptr<Expression>);
-std::unique_ptr<Expression> ne(std::unique_ptr<Expression>,
- std::unique_ptr<Expression>);
-std::unique_ptr<Expression> gt(std::unique_ptr<Expression>,
- std::unique_ptr<Expression>);
-std::unique_ptr<Expression> lt(std::unique_ptr<Expression>,
- std::unique_ptr<Expression>);
+std::unique_ptr<Expression> eq(std::unique_ptr<Expression>, std::unique_ptr<Expression>);
+std::unique_ptr<Expression> ne(std::unique_ptr<Expression>, std::unique_ptr<Expression>);
+std::unique_ptr<Expression> gt(std::unique_ptr<Expression>, std::unique_ptr<Expression>);
+std::unique_ptr<Expression> lt(std::unique_ptr<Expression>, std::unique_ptr<Expression>);
std::unique_ptr<Expression> step(std::unique_ptr<Expression> input,
std::unique_ptr<Expression> output0,
@@ -86,7 +85,7 @@ std::unique_ptr<Expression> interpolate(Interpolator interpolator,
double input3, std::unique_ptr<Expression> output3);
std::unique_ptr<Expression> concat(std::vector<std::unique_ptr<Expression>> inputs);
-
+
std::unique_ptr<Expression> format(const char* value);
std::unique_ptr<Expression> format(std::unique_ptr<Expression>);
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
index 5f66fc6dc7..ad57748677 100644
--- a/include/mbgl/style/expression/expression.hpp
+++ b/include/mbgl/style/expression/expression.hpp
@@ -31,6 +31,9 @@ public:
EvaluationContext(float zoom_, GeometryTileFeature const * feature_) :
zoom(zoom_), feature(feature_)
{}
+ EvaluationContext(optional<mbgl::Value> accumulated_, GeometryTileFeature const * feature_) :
+ accumulated(std::move(accumulated_)), feature(feature_)
+ {}
EvaluationContext(optional<float> zoom_, GeometryTileFeature const * feature_, optional<double> colorRampParameter_) :
zoom(std::move(zoom_)), feature(feature_), colorRampParameter(std::move(colorRampParameter_))
{}
@@ -41,6 +44,7 @@ public:
};
optional<float> zoom;
+ optional<mbgl::Value> accumulated;
GeometryTileFeature const * feature = nullptr;
optional<double> colorRampParameter;
// Contains formatted section object, std::unordered_map<std::string, Value>.
@@ -162,7 +166,7 @@ public:
type::Type getType() const { return type; };
EvaluationResult evaluate(optional<float> zoom, const Feature& feature, optional<double> colorRampParameter) const;
-
+ EvaluationResult evaluate(optional<mbgl::Value> accumulated, const Feature& feature) const;
/**
* Statically analyze the expression, attempting to enumerate possible outputs. Returns
* an array of values plus the sentinel null optional value, used to indicate that the
diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp
index a66ff37b22..ecd3f01f70 100644
--- a/include/mbgl/style/layer.hpp
+++ b/include/mbgl/style/layer.hpp
@@ -1,11 +1,13 @@
#pragma once
-#include <mbgl/util/peer.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mapbox/weak.hpp>
+#include <mapbox/type_wrapper.hpp>
+
#include <cassert>
#include <memory>
#include <string>
@@ -119,15 +121,20 @@ public:
// For use in SDK bindings, which store a reference to a platform-native peer
// object here, so that separately-obtained references to this object share
// identical platform-native peers.
- util::peer peer;
+ mapbox::base::TypeWrapper peer;
Layer(Immutable<Impl>);
const LayerTypeInfo* getTypeInfo() const noexcept;
+ mapbox::base::WeakPtr<Layer> makeWeakPtr() {
+ return weakFactory.makeWeakPtr();
+ }
+
protected:
virtual Mutable<Impl> mutableBaseImpl() const = 0;
LayerObserver* observer;
+ mapbox::base::WeakPtrFactory<Layer> weakFactory {this};
};
} // namespace style
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index 2493df6046..853c0b282e 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -186,6 +186,10 @@ public:
const PropertyValue<std::vector<TextVariableAnchorType>>& getTextVariableAnchor() const;
void setTextVariableAnchor(const PropertyValue<std::vector<TextVariableAnchorType>>&);
+ static PropertyValue<std::vector<TextWritingModeType>> getDefaultTextWritingMode();
+ const PropertyValue<std::vector<TextWritingModeType>>& getTextWritingMode() const;
+ void setTextWritingMode(const PropertyValue<std::vector<TextWritingModeType>>&);
+
// Paint properties
static PropertyValue<Color> getDefaultIconColor();
diff --git a/include/mbgl/style/light.hpp b/include/mbgl/style/light.hpp
index 0b65df9f1b..21a05cf4c8 100644
--- a/include/mbgl/style/light.hpp
+++ b/include/mbgl/style/light.hpp
@@ -2,6 +2,7 @@
#pragma once
+#include <mbgl/style/conversion.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/types.hpp>
@@ -17,6 +18,9 @@ public:
Light();
~Light();
+ // Dynamic properties
+ optional<conversion::Error> setProperty(const std::string& name, const conversion::Convertible& value);
+
static LightAnchorType getDefaultAnchor();
PropertyValue<LightAnchorType> getAnchor() const;
void setAnchor(PropertyValue<LightAnchorType>);
diff --git a/include/mbgl/style/light.hpp.ejs b/include/mbgl/style/light.hpp.ejs
index adc5b651e3..c3001d982b 100644
--- a/include/mbgl/style/light.hpp.ejs
+++ b/include/mbgl/style/light.hpp.ejs
@@ -5,6 +5,7 @@
#pragma once
+#include <mbgl/style/conversion.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/types.hpp>
@@ -20,6 +21,9 @@ public:
Light();
~Light();
+ // Dynamic properties
+ optional<conversion::Error> setProperty(const std::string& name, const conversion::Convertible& value);
+
<% for (const property of properties) { -%>
static <%- evaluatedType(property) %> getDefault<%- camelize(property.name) %>();
<%- propertyValueType(property) %> get<%- camelize(property.name) %>() const;
diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp
index dc3a8d73fb..2507b67fdc 100644
--- a/include/mbgl/style/source.hpp
+++ b/include/mbgl/style/source.hpp
@@ -2,10 +2,12 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/peer.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/style/types.hpp>
+#include <mapbox/weak.hpp>
+#include <mapbox/type_wrapper.hpp>
+
#include <memory>
#include <string>
@@ -77,7 +79,9 @@ public:
// For use in SDK bindings, which store a reference to a platform-native peer
// object here, so that separately-obtained references to this object share
// identical platform-native peers.
- util::peer peer;
+ mapbox::base::TypeWrapper peer;
+
+ virtual mapbox::base::WeakPtr<Source> makeWeakPtr() = 0;
};
} // namespace style
diff --git a/include/mbgl/style/sources/custom_geometry_source.hpp b/include/mbgl/style/sources/custom_geometry_source.hpp
index 3f29d0c0fb..a5e545f445 100644
--- a/include/mbgl/style/sources/custom_geometry_source.hpp
+++ b/include/mbgl/style/sources/custom_geometry_source.hpp
@@ -20,7 +20,7 @@ using TileFunction = std::function<void(const CanonicalTileID&)>;
class CustomTileLoader;
-class CustomGeometrySource : public Source {
+class CustomGeometrySource final : public Source {
public:
struct TileOptions {
double tolerance = 0.375;
@@ -46,9 +46,13 @@ public:
// Private implementation
class Impl;
const Impl& impl() const;
+ mapbox::base::WeakPtr<Source> makeWeakPtr() override {
+ return weakFactory.makeWeakPtr();
+ }
private:
std::shared_ptr<ThreadPool> threadPool;
std::unique_ptr<Actor<CustomTileLoader>> loader;
+ mapbox::base::WeakPtrFactory<Source> weakFactory {this};
};
template <>
diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp
index a03b910279..c99687fad6 100644
--- a/include/mbgl/style/sources/geojson_source.hpp
+++ b/include/mbgl/style/sources/geojson_source.hpp
@@ -1,9 +1,14 @@
#pragma once
+#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/source.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/constants.hpp>
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
namespace mbgl {
@@ -24,11 +29,15 @@ struct GeoJSONOptions {
bool cluster = false;
uint16_t clusterRadius = 50;
uint8_t clusterMaxZoom = 17;
+ using ClusterExpression = std::pair<std::shared_ptr<mbgl::style::expression::Expression>,
+ std::shared_ptr<mbgl::style::expression::Expression>>;
+ using ClusterProperties = std::unordered_map<std::string, ClusterExpression>;
+ ClusterProperties clusterProperties;
};
-class GeoJSONSource : public Source {
+class GeoJSONSource final : public Source {
public:
- GeoJSONSource(const std::string& id, const GeoJSONOptions& = {});
+ GeoJSONSource(const std::string& id, optional<GeoJSONOptions> = nullopt);
~GeoJSONSource() final;
void setURL(const std::string& url);
@@ -41,9 +50,14 @@ public:
void loadDescription(FileSource&) final;
+ mapbox::base::WeakPtr<Source> makeWeakPtr() override {
+ return weakFactory.makeWeakPtr();
+ }
+
private:
optional<std::string> url;
std::unique_ptr<AsyncRequest> req;
+ mapbox::base::WeakPtrFactory<Source> weakFactory {this};
};
template <>
diff --git a/include/mbgl/style/sources/image_source.hpp b/include/mbgl/style/sources/image_source.hpp
index 009764291f..84faab33c9 100644
--- a/include/mbgl/style/sources/image_source.hpp
+++ b/include/mbgl/style/sources/image_source.hpp
@@ -10,7 +10,7 @@ class AsyncRequest;
namespace style {
-class ImageSource : public Source {
+class ImageSource final : public Source {
public:
ImageSource(std::string id, const std::array<LatLng, 4>);
~ImageSource() override;
@@ -27,9 +27,14 @@ public:
const Impl& impl() const;
void loadDescription(FileSource&) final;
+
+ mapbox::base::WeakPtr<Source> makeWeakPtr() override {
+ return weakFactory.makeWeakPtr();
+ }
private:
optional<std::string> url;
std::unique_ptr<AsyncRequest> req;
+ mapbox::base::WeakPtrFactory<Source> weakFactory {this};
};
template <>
diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp
index 5aa81aa979..1bdced8da7 100644
--- a/include/mbgl/style/sources/raster_source.hpp
+++ b/include/mbgl/style/sources/raster_source.hpp
@@ -25,9 +25,14 @@ public:
void loadDescription(FileSource&) final;
+ mapbox::base::WeakPtr<Source> makeWeakPtr() final {
+ return weakFactory.makeWeakPtr();
+ }
+
private:
const variant<std::string, Tileset> urlOrTileset;
std::unique_ptr<AsyncRequest> req;
+ mapbox::base::WeakPtrFactory<Source> weakFactory {this};
};
template <>
diff --git a/include/mbgl/style/sources/vector_source.hpp b/include/mbgl/style/sources/vector_source.hpp
index 6f16974b40..97f0a7e5a8 100644
--- a/include/mbgl/style/sources/vector_source.hpp
+++ b/include/mbgl/style/sources/vector_source.hpp
@@ -10,9 +10,10 @@ class AsyncRequest;
namespace style {
-class VectorSource : public Source {
+class VectorSource final : public Source {
public:
- VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset);
+ VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset, optional<float> maxZoom = nullopt,
+ optional<float> minZoom = nullopt);
~VectorSource() final;
const variant<std::string, Tileset>& getURLOrTileset() const;
@@ -23,9 +24,16 @@ public:
void loadDescription(FileSource&) final;
+ mapbox::base::WeakPtr<Source> makeWeakPtr() override {
+ return weakFactory.makeWeakPtr();
+ }
+
private:
const variant<std::string, Tileset> urlOrTileset;
std::unique_ptr<AsyncRequest> req;
+ mapbox::base::WeakPtrFactory<Source> weakFactory {this};
+ optional<float> maxZoom;
+ optional<float> minZoom;
};
template <>
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index 13a2e50f01..5d88dafb33 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -115,6 +115,11 @@ enum class IconTextFitType : uint8_t {
Height
};
+enum class TextWritingModeType : uint8_t {
+ Horizontal,
+ Vertical
+};
+
enum class LightAnchorType: bool {
Map,
Viewport
diff --git a/include/mbgl/util/bitmask_operations.hpp b/include/mbgl/util/bitmask_operations.hpp
new file mode 100644
index 0000000000..014fabdea2
--- /dev/null
+++ b/include/mbgl/util/bitmask_operations.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <mbgl/util/traits.hpp>
+#include <mbgl/util/util.hpp>
+
+namespace mbgl {
+
+template<typename Enum>
+MBGL_CONSTEXPR Enum operator|(Enum a, Enum b) {
+ static_assert(std::is_enum<Enum>::value, "Enum must be an enum type");
+ return Enum(mbgl::underlying_type(a) | mbgl::underlying_type(b));
+}
+
+template<typename Enum>
+MBGL_CONSTEXPR Enum& operator|=(Enum& a, Enum b) {
+ static_assert(std::is_enum<Enum>::value, "Enum must be an enum type");
+ return (a = a | b);
+}
+
+template<typename Enum>
+MBGL_CONSTEXPR bool operator&(Enum a, Enum b) {
+ static_assert(std::is_enum<Enum>::value, "Enum must be an enum type");
+ return bool(mbgl::underlying_type(a) & mbgl::underlying_type(b));
+}
+
+template<typename Enum>
+MBGL_CONSTEXPR Enum operator~(Enum value) {
+ static_assert(std::is_enum<Enum>::value, "Enum must be an enum type");
+ return Enum(~mbgl::underlying_type(value));
+}
+
+
+} // namespace mbgl \ No newline at end of file
diff --git a/include/mbgl/util/color.hpp b/include/mbgl/util/color.hpp
index 01a4c8f292..1ff44ce085 100644
--- a/include/mbgl/util/color.hpp
+++ b/include/mbgl/util/color.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/feature.hpp>
#include <cassert>
#include <string>
@@ -39,6 +40,7 @@ public:
static optional<Color> parse(const std::string&);
std::string stringify() const;
std::array<double, 4> toArray() const;
+ mbgl::Value toObject() const;
};
inline bool operator==(const Color& colorA, const Color& colorB) {
diff --git a/platform/default/include/mbgl/util/default_styles.hpp b/include/mbgl/util/default_styles.hpp
index 335d3ea5f9..e1c53178b0 100644
--- a/platform/default/include/mbgl/util/default_styles.hpp
+++ b/include/mbgl/util/default_styles.hpp
@@ -1,7 +1,8 @@
#pragma once
-#include <vector>
+#include <array>
#include <string>
+#include <vector>
namespace mbgl {
namespace util {
@@ -13,17 +14,19 @@ struct DefaultStyle {
const unsigned currentVersion;
};
+// clang-format off
constexpr const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v11", "Streets", 11 };
constexpr const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v11", "Outdoors", 11 };
constexpr const DefaultStyle light = { "mapbox://styles/mapbox/light-v10", "Light", 10 };
constexpr const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v10", "Dark", 10 };
constexpr const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 };
constexpr const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v11", "Satellite Streets", 11 };
+// clang-format on
-const DefaultStyle orderedStyles[] = {
- streets, outdoors, light, dark, satellite, satelliteStreets,
-};
-const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle);
+constexpr std::array<const DefaultStyle, 6> orderedStyles = {
+ {streets, outdoors, light, dark, satellite, satelliteStreets}};
+
+constexpr size_t numOrderedStyles = orderedStyles.size();
} // end namespace default_styles
} // end namespace util
diff --git a/include/mbgl/util/peer.hpp b/include/mbgl/util/peer.hpp
deleted file mode 100644
index d16efddb57..0000000000
--- a/include/mbgl/util/peer.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-namespace mbgl {
-namespace util {
-
-class peer {
-public:
- peer() noexcept : storage(nullptr, noop_deleter) {}
-
- template <class T>
- peer(T&& value) noexcept : storage(new std::decay_t<T>(std::forward<T>(value)), cast_deleter<std::decay_t<T>>) {
- static_assert(!std::is_same<peer, std::decay_t<T>>::value, "Peer must not wrap itself.");
- }
-
- bool has_value() const noexcept { return static_cast<bool>(storage); }
-
- template <class T>
- T& get() noexcept { return *reinterpret_cast<T*>(storage.get()); }
-
-private:
- template <typename T>
- static void cast_deleter(void* ptr) noexcept { delete reinterpret_cast<T*>(ptr); }
- static void noop_deleter(void*) noexcept {}
-
- using storage_t = std::unique_ptr<void, void(*)(void*)>;
- storage_t storage;
-};
-
-} // namespace util
-} // namespace mbgl
diff --git a/include/mbgl/util/util.hpp b/include/mbgl/util/util.hpp
index 7960b40299..52d9ad85b7 100644
--- a/include/mbgl/util/util.hpp
+++ b/include/mbgl/util/util.hpp
@@ -19,3 +19,26 @@
#else
#define MBGL_CONSTEXPR inline
#endif
+
+// Compiler defines for making symbols visible, otherwise they
+// will be defined as hidden by default.
+
+// clang-format off
+#if defined WIN32
+ #ifdef MBGL_BUILDING_LIB
+ #ifdef __GNUC__
+ #define MBGL_EXPORT __attribute__((dllexport))
+ #else
+ #define MBGL_EXPORT __declspec(dllexport)
+ #endif
+ #else
+ #ifdef __GNUC__
+ #define MBGL_EXPORT __attribute__((dllimport))
+ #else
+ #define MBGL_EXPORT __declspec(dllimport)
+ #endif
+ #endif
+#else
+ #define MBGL_EXPORT __attribute__((visibility ("default"))) // NOLINT
+#endif
+// clang-format on
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject 3abd5d518bade8d83eb609598b861432a83f869
+Subproject 572bbb55757c93a9f11fa9b0d0ca099bc4ec361
diff --git a/misc/buck/mapbox-gl-native/BUCK b/misc/buck/mapbox-gl-native/BUCK
index ed264e9756..0712385773 100644
--- a/misc/buck/mapbox-gl-native/BUCK
+++ b/misc/buck/mapbox-gl-native/BUCK
@@ -43,18 +43,12 @@ mbgl_vendor_library("cheap-ruler-cpp")
mbgl_vendor_library("earcut.hpp")
mbgl_vendor_library("expected")
mbgl_vendor_library("eternal")
-mbgl_vendor_library("geojson.hpp")
mbgl_vendor_library("geojson-vt-cpp")
-mbgl_vendor_library("jni.hpp")
-mbgl_vendor_library("kdbush.hpp")
mbgl_vendor_library("mapbox-base")
mbgl_vendor_library("sqlite")
-mbgl_vendor_library("pixelmatch-cpp")
mbgl_vendor_library("polylabel")
mbgl_vendor_library("protozero")
-mbgl_vendor_library("rapidjson")
mbgl_vendor_library("shelf-pack-cpp")
-mbgl_vendor_library("supercluster.hpp")
mbgl_vendor_library("unique_resource")
mbgl_vendor_library("vector-tile")
mbgl_vendor_library("wagyu")
diff --git a/next/CMakeLists.txt b/next/CMakeLists.txt
new file mode 100644
index 0000000000..150dbc6dbd
--- /dev/null
+++ b/next/CMakeLists.txt
@@ -0,0 +1,941 @@
+cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
+project("Mapbox GL Native" LANGUAGES CXX C)
+
+# TODO: Remove when PROJECT_SOURCE_DIR becomes the actual root.
+set(MBGL_ROOT ${PROJECT_SOURCE_DIR}/..)
+
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER Core)
+
+enable_testing()
+
+set(
+ CMAKE_CONFIGURATION_TYPES
+ Debug
+ DebugCoverage
+ MinSizeRel
+ RelWithDebInfo
+ Release
+ Sanitize
+)
+
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+
+option(MBGL_WITH_CORE_ONLY "Build only the core bits, no platform code" OFF)
+option(MBGL_WITH_QT "Build Mapbox GL Qt bindings" OFF)
+option(MBGL_WITH_SANITIZER "Use [address|thread|memory|undefined] here" OFF)
+
+set(CMAKE_CXX_FLAGS_DEBUGCOVERAGE "${CMAKE_CXX_FLAGS_DEBUG} --coverage")
+set(CMAKE_C_FLAGS_DEBUGCOVERAGE "${CMAKE_CXX_FLAGS_DEBUG} --coverage")
+
+set(CMAKE_CXX_FLAGS_SANITIZE "-DSANITIZE -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=${MBGL_WITH_SANITIZER}")
+set(CMAKE_C_FLAGS_SANITIZE "-DSANITEIZE -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=${MBGL_WITH_SANITIZER}")
+
+if(MBGL_WITH_QT)
+ find_package(Qt5Core REQUIRED)
+ set(CMAKE_AUTOMOC ON)
+ set(CMAKE_AUTORCC ON)
+endif()
+
+# Avoid warnings when setting visibility
+cmake_policy(SET CMP0063 NEW)
+
+# Compiler/linker configuration
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+set(CMAKE_C_EXTENSIONS OFF)
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(CMAKE_C_VISIBILITY_PRESET hidden)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
+
+add_library(
+ mbgl-core STATIC
+ ${MBGL_ROOT}/include/mbgl/actor/actor.hpp
+ ${MBGL_ROOT}/include/mbgl/actor/actor_ref.hpp
+ ${MBGL_ROOT}/include/mbgl/actor/aspiring_actor.hpp
+ ${MBGL_ROOT}/include/mbgl/actor/established_actor.hpp
+ ${MBGL_ROOT}/include/mbgl/actor/mailbox.hpp
+ ${MBGL_ROOT}/include/mbgl/actor/message.hpp
+ ${MBGL_ROOT}/include/mbgl/actor/scheduler.hpp
+ ${MBGL_ROOT}/include/mbgl/annotation/annotation.hpp
+ ${MBGL_ROOT}/include/mbgl/gfx/backend.hpp
+ ${MBGL_ROOT}/include/mbgl/gfx/backend_scope.hpp
+ ${MBGL_ROOT}/include/mbgl/gfx/renderable.hpp
+ ${MBGL_ROOT}/include/mbgl/gfx/renderer_backend.hpp
+ ${MBGL_ROOT}/include/mbgl/gl/renderable_resource.hpp
+ ${MBGL_ROOT}/include/mbgl/gl/renderer_backend.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/background_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/circle_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/custom_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/fill_extrusion_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/fill_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/heatmap_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/hillshade_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/layer_manager.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/line_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/raster_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/layermanager/symbol_layer_factory.hpp
+ ${MBGL_ROOT}/include/mbgl/map/bound_options.hpp
+ ${MBGL_ROOT}/include/mbgl/map/camera.hpp
+ ${MBGL_ROOT}/include/mbgl/map/change.hpp
+ ${MBGL_ROOT}/include/mbgl/map/map.hpp
+ ${MBGL_ROOT}/include/mbgl/map/map_observer.hpp
+ ${MBGL_ROOT}/include/mbgl/map/map_options.hpp
+ ${MBGL_ROOT}/include/mbgl/map/mode.hpp
+ ${MBGL_ROOT}/include/mbgl/map/projection_mode.hpp
+ ${MBGL_ROOT}/include/mbgl/math/clamp.hpp
+ ${MBGL_ROOT}/include/mbgl/math/log2.hpp
+ ${MBGL_ROOT}/include/mbgl/math/minmax.hpp
+ ${MBGL_ROOT}/include/mbgl/math/wrap.hpp
+ ${MBGL_ROOT}/include/mbgl/platform/gl_functions.hpp
+ ${MBGL_ROOT}/include/mbgl/platform/thread.hpp
+ ${MBGL_ROOT}/include/mbgl/renderer/query.hpp
+ ${MBGL_ROOT}/include/mbgl/renderer/renderer.hpp
+ ${MBGL_ROOT}/include/mbgl/renderer/renderer_frontend.hpp
+ ${MBGL_ROOT}/include/mbgl/renderer/renderer_observer.hpp
+ ${MBGL_ROOT}/include/mbgl/renderer/renderer_state.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/default_file_source.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/file_source.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/network_status.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/offline.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/online_file_source.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/resource.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/resource_options.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/resource_transform.hpp
+ ${MBGL_ROOT}/include/mbgl/storage/response.hpp
+ ${MBGL_ROOT}/include/mbgl/style/color_ramp_property_value.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/color_ramp_property_value.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/constant.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/coordinate.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/custom_geometry_source_options.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/filter.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/function.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/geojson.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/geojson_options.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/get_json_type.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/light.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/position.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/property_value.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/tileset.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion/transition_options.hpp
+ ${MBGL_ROOT}/include/mbgl/style/conversion_impl.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/assertion.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/at.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/boolean_operator.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/case.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/check_subtype.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/coalesce.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/coercion.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/collator.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/collator_expression.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/comparison.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/compound_expression.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/dsl.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/error.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/expression.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/find_zoom_curve.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/format_expression.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/format_section_override.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/formatted.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/get_covering_stops.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/interpolate.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/interpolator.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/is_constant.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/is_expression.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/length.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/let.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/literal.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/match.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/number_format.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/parsing_context.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/step.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/type.hpp
+ ${MBGL_ROOT}/include/mbgl/style/expression/value.hpp
+ ${MBGL_ROOT}/include/mbgl/style/filter.hpp
+ ${MBGL_ROOT}/include/mbgl/style/image.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layer_properties.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/background_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/circle_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/custom_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/fill_extrusion_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/fill_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/heatmap_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/hillshade_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/line_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/raster_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/layers/symbol_layer.hpp
+ ${MBGL_ROOT}/include/mbgl/style/light.hpp
+ ${MBGL_ROOT}/include/mbgl/style/position.hpp
+ ${MBGL_ROOT}/include/mbgl/style/property_expression.hpp
+ ${MBGL_ROOT}/include/mbgl/style/property_value.hpp
+ ${MBGL_ROOT}/include/mbgl/style/source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/sources/custom_geometry_source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/sources/geojson_source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/sources/image_source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/sources/raster_dem_source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/sources/raster_source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/sources/vector_source.hpp
+ ${MBGL_ROOT}/include/mbgl/style/style.hpp
+ ${MBGL_ROOT}/include/mbgl/style/transition_options.hpp
+ ${MBGL_ROOT}/include/mbgl/style/types.hpp
+ ${MBGL_ROOT}/include/mbgl/style/undefined.hpp
+ ${MBGL_ROOT}/include/mbgl/tile/tile_id.hpp
+ ${MBGL_ROOT}/include/mbgl/tile/tile_necessity.hpp
+ ${MBGL_ROOT}/include/mbgl/util/async_request.hpp
+ ${MBGL_ROOT}/include/mbgl/util/async_task.hpp
+ ${MBGL_ROOT}/include/mbgl/util/char_array_buffer.hpp
+ ${MBGL_ROOT}/include/mbgl/util/chrono.hpp
+ ${MBGL_ROOT}/include/mbgl/util/color.hpp
+ ${MBGL_ROOT}/include/mbgl/util/compression.hpp
+ ${MBGL_ROOT}/include/mbgl/util/constants.hpp
+ ${MBGL_ROOT}/include/mbgl/util/convert.hpp
+ ${MBGL_ROOT}/include/mbgl/util/enum.hpp
+ ${MBGL_ROOT}/include/mbgl/util/event.hpp
+ ${MBGL_ROOT}/include/mbgl/util/exception.hpp
+ ${MBGL_ROOT}/include/mbgl/util/expected.hpp
+ ${MBGL_ROOT}/include/mbgl/util/feature.hpp
+ ${MBGL_ROOT}/include/mbgl/util/font_stack.hpp
+ ${MBGL_ROOT}/include/mbgl/util/geo.hpp
+ ${MBGL_ROOT}/include/mbgl/util/geojson.hpp
+ ${MBGL_ROOT}/include/mbgl/util/geometry.hpp
+ ${MBGL_ROOT}/include/mbgl/util/ignore.hpp
+ ${MBGL_ROOT}/include/mbgl/util/image.hpp
+ ${MBGL_ROOT}/include/mbgl/util/immutable.hpp
+ ${MBGL_ROOT}/include/mbgl/util/indexed_tuple.hpp
+ ${MBGL_ROOT}/include/mbgl/util/interpolate.hpp
+ ${MBGL_ROOT}/include/mbgl/util/logging.hpp
+ ${MBGL_ROOT}/include/mbgl/util/noncopyable.hpp
+ ${MBGL_ROOT}/include/mbgl/util/optional.hpp
+ ${MBGL_ROOT}/include/mbgl/util/platform.hpp
+ ${MBGL_ROOT}/include/mbgl/util/premultiply.hpp
+ ${MBGL_ROOT}/include/mbgl/util/projection.hpp
+ ${MBGL_ROOT}/include/mbgl/util/range.hpp
+ ${MBGL_ROOT}/include/mbgl/util/run_loop.hpp
+ ${MBGL_ROOT}/include/mbgl/util/size.hpp
+ ${MBGL_ROOT}/include/mbgl/util/string.hpp
+ ${MBGL_ROOT}/include/mbgl/util/thread.hpp
+ ${MBGL_ROOT}/include/mbgl/util/tileset.hpp
+ ${MBGL_ROOT}/include/mbgl/util/timer.hpp
+ ${MBGL_ROOT}/include/mbgl/util/traits.hpp
+ ${MBGL_ROOT}/include/mbgl/util/type_list.hpp
+ ${MBGL_ROOT}/include/mbgl/util/unitbezier.hpp
+ ${MBGL_ROOT}/include/mbgl/util/util.hpp
+ ${MBGL_ROOT}/include/mbgl/util/variant.hpp
+ ${MBGL_ROOT}/include/mbgl/util/work_request.hpp
+ ${MBGL_ROOT}/include/mbgl/util/work_task.hpp
+ ${MBGL_ROOT}/include/mbgl/util/work_task_impl.hpp
+ ${MBGL_ROOT}/src/csscolorparser/csscolorparser.cpp
+ ${MBGL_ROOT}/src/csscolorparser/csscolorparser.hpp
+ ${MBGL_ROOT}/src/mbgl/actor/mailbox.cpp
+ ${MBGL_ROOT}/src/mbgl/actor/scheduler.cpp
+ ${MBGL_ROOT}/src/mbgl/algorithm/update_renderables.hpp
+ ${MBGL_ROOT}/src/mbgl/algorithm/update_tile_masks.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/annotation_manager.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/annotation_manager.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/annotation_source.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/annotation_source.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/annotation_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/annotation_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/fill_annotation_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/fill_annotation_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/line_annotation_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/line_annotation_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/render_annotation_source.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/render_annotation_source.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/shape_annotation_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/shape_annotation_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/annotation/symbol_annotation_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/annotation/symbol_annotation_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/geometry/anchor.hpp
+ ${MBGL_ROOT}/src/mbgl/geometry/debug_font_data.hpp
+ ${MBGL_ROOT}/src/mbgl/geometry/dem_data.cpp
+ ${MBGL_ROOT}/src/mbgl/geometry/dem_data.hpp
+ ${MBGL_ROOT}/src/mbgl/geometry/feature_index.cpp
+ ${MBGL_ROOT}/src/mbgl/geometry/feature_index.hpp
+ ${MBGL_ROOT}/src/mbgl/geometry/line_atlas.cpp
+ ${MBGL_ROOT}/src/mbgl/geometry/line_atlas.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/attribute.cpp
+ ${MBGL_ROOT}/src/mbgl/gfx/attribute.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/color_mode.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/command_encoder.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/context.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/cull_face_mode.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/debug_group.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/depth_mode.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/draw_mode.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/draw_scope.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/index_buffer.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/index_vector.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/offscreen_texture.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/program.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/render_pass.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/renderbuffer.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/renderer_backend.cpp
+ ${MBGL_ROOT}/src/mbgl/gfx/stencil_mode.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/texture.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/types.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/uniform.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/upload_pass.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/vertex_buffer.hpp
+ ${MBGL_ROOT}/src/mbgl/gfx/vertex_vector.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/attribute.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/attribute.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/command_encoder.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/command_encoder.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/context.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/context.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/debugging_extension.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/debugging_extension.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/defines.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/draw_scope_resource.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/enum.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/enum.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/extension.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/framebuffer.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/index_buffer_resource.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/object.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/object.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/offscreen_texture.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/offscreen_texture.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/program.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/render_pass.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/render_pass.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/renderbuffer_resource.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/renderer_backend.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/state.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/texture.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/texture.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/texture_resource.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/types.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/uniform.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/uniform.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/upload_pass.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/upload_pass.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/value.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/value.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/vertex_array.cpp
+ ${MBGL_ROOT}/src/mbgl/gl/vertex_array.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/vertex_array_extension.hpp
+ ${MBGL_ROOT}/src/mbgl/gl/vertex_buffer_resource.hpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/background_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/circle_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/custom_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/fill_extrusion_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/fill_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/heatmap_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/hillshade_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/layer_manager.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/line_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/raster_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layermanager/symbol_layer_factory.cpp
+ ${MBGL_ROOT}/src/mbgl/layout/clip_lines.cpp
+ ${MBGL_ROOT}/src/mbgl/layout/clip_lines.hpp
+ ${MBGL_ROOT}/src/mbgl/layout/layout.hpp
+ ${MBGL_ROOT}/src/mbgl/layout/merge_lines.cpp
+ ${MBGL_ROOT}/src/mbgl/layout/merge_lines.hpp
+ ${MBGL_ROOT}/src/mbgl/layout/pattern_layout.hpp
+ ${MBGL_ROOT}/src/mbgl/layout/symbol_feature.hpp
+ ${MBGL_ROOT}/src/mbgl/layout/symbol_instance.cpp
+ ${MBGL_ROOT}/src/mbgl/layout/symbol_instance.hpp
+ ${MBGL_ROOT}/src/mbgl/layout/symbol_layout.cpp
+ ${MBGL_ROOT}/src/mbgl/layout/symbol_layout.hpp
+ ${MBGL_ROOT}/src/mbgl/layout/symbol_projection.cpp
+ ${MBGL_ROOT}/src/mbgl/layout/symbol_projection.hpp
+ ${MBGL_ROOT}/src/mbgl/map/map.cpp
+ ${MBGL_ROOT}/src/mbgl/map/map_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/map/map_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/map/map_options.cpp
+ ${MBGL_ROOT}/src/mbgl/map/transform.cpp
+ ${MBGL_ROOT}/src/mbgl/map/transform.hpp
+ ${MBGL_ROOT}/src/mbgl/map/transform_state.cpp
+ ${MBGL_ROOT}/src/mbgl/map/transform_state.hpp
+ ${MBGL_ROOT}/src/mbgl/map/zoom_history.hpp
+ ${MBGL_ROOT}/src/mbgl/math/log2.cpp
+ ${MBGL_ROOT}/src/mbgl/platform/gl_functions.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/attributes.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/background_pattern_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/background_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/background_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/circle_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/circle_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/clipping_mask_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/clipping_mask_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/collision_box_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/collision_box_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/collision_circle_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/debug_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/debug_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_extrusion_pattern_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_extrusion_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_extrusion_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_outline_pattern_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_outline_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_pattern_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/fill_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/background.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/background_pattern.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/circle.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/clipping_mask.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/collision_box.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/collision_circle.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/debug.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/fill.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/fill_extrusion.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/fill_extrusion_pattern.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/fill_outline.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/fill_outline_pattern.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/fill_pattern.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/heatmap.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/heatmap_texture.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/hillshade.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/hillshade_prepare.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/line.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/line_gradient.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/line_pattern.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/line_sdf.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/preludes.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/raster.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/shader_source.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/shader_source.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/shaders.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/shaders.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/symbol_icon.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/symbol_sdf_icon.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/gl/symbol_sdf_text.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/heatmap_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/heatmap_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/heatmap_texture_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/heatmap_texture_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/hillshade_prepare_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/hillshade_prepare_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/hillshade_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/hillshade_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/line_gradient_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/line_pattern_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/line_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/line_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/line_sdf_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/program_parameters.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/program_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/programs.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/programs.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/raster_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/raster_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/segment.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/symbol_icon_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/symbol_program.cpp
+ ${MBGL_ROOT}/src/mbgl/programs/symbol_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/symbol_sdf_icon_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/symbol_sdf_text_program.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/textures.hpp
+ ${MBGL_ROOT}/src/mbgl/programs/uniforms.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/backend_scope.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/bucket_parameters.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/bucket_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/circle_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/circle_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/debug_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/debug_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/fill_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/fill_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/heatmap_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/heatmap_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/hillshade_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/hillshade_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/line_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/line_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/raster_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/raster_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/symbol_bucket.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/buckets/symbol_bucket.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/cross_faded_property_evaluator.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/cross_faded_property_evaluator.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/data_driven_property_evaluator.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/group_by_layout.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/group_by_layout.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/image_atlas.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/image_atlas.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/image_manager.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/image_manager.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/image_manager_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_background_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_background_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_circle_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_circle_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_custom_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_custom_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_fill_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_fill_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_heatmap_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_heatmap_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_hillshade_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_hillshade_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_line_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_line_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_raster_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_raster_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_symbol_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/layers/render_symbol_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/paint_parameters.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/paint_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/paint_property_binder.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/paint_property_statistics.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/pattern_atlas.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/pattern_atlas.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/possibly_evaluated_property_value.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/property_evaluation_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/property_evaluator.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_layer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_light.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_light.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_orchestrator.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_orchestrator.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_pass.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_source_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_static_data.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_static_data.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/render_tree.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/renderer.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/renderer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/renderer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/renderer_state.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_geojson_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_geojson_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_image_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_image_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_raster_dem_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_raster_dem_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_raster_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_raster_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_tile_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_tile_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_vector_source.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/sources/render_vector_source.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/style_diff.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/style_diff.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/tile_mask.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/tile_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/tile_pyramid.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/tile_pyramid.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/tile_render_data.cpp
+ ${MBGL_ROOT}/src/mbgl/renderer/tile_render_data.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/transition_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/update_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/renderer/upload_parameters.hpp
+ ${MBGL_ROOT}/src/mbgl/sprite/sprite_loader.cpp
+ ${MBGL_ROOT}/src/mbgl/sprite/sprite_loader.hpp
+ ${MBGL_ROOT}/src/mbgl/sprite/sprite_loader_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/sprite/sprite_loader_worker.cpp
+ ${MBGL_ROOT}/src/mbgl/sprite/sprite_loader_worker.hpp
+ ${MBGL_ROOT}/src/mbgl/sprite/sprite_parser.cpp
+ ${MBGL_ROOT}/src/mbgl/sprite/sprite_parser.hpp
+ ${MBGL_ROOT}/src/mbgl/storage/asset_file_source.hpp
+ ${MBGL_ROOT}/src/mbgl/storage/file_source.cpp
+ ${MBGL_ROOT}/src/mbgl/storage/http_file_source.hpp
+ ${MBGL_ROOT}/src/mbgl/storage/local_file_source.hpp
+ ${MBGL_ROOT}/src/mbgl/storage/network_status.cpp
+ ${MBGL_ROOT}/src/mbgl/storage/resource.cpp
+ ${MBGL_ROOT}/src/mbgl/storage/resource_options.cpp
+ ${MBGL_ROOT}/src/mbgl/storage/resource_transform.cpp
+ ${MBGL_ROOT}/src/mbgl/storage/response.cpp
+ ${MBGL_ROOT}/src/mbgl/style/collection.hpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/color_ramp_property_value.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/constant.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/coordinate.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/custom_geometry_source_options.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/filter.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/function.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/geojson.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/geojson_options.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/get_json_type.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/json.hpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/light.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/position.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/property_value.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/stringify.hpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/tileset.cpp
+ ${MBGL_ROOT}/src/mbgl/style/conversion/transition_options.cpp
+ ${MBGL_ROOT}/src/mbgl/style/custom_tile_loader.cpp
+ ${MBGL_ROOT}/src/mbgl/style/custom_tile_loader.hpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/assertion.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/at.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/boolean_operator.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/case.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/check_subtype.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/coalesce.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/coercion.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/collator_expression.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/comparison.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/compound_expression.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/dsl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/dsl_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/expression.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/find_zoom_curve.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/format_expression.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/formatted.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/get_covering_stops.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/interpolate.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/is_constant.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/is_expression.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/length.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/let.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/literal.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/match.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/number_format.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/parsing_context.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/step.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/util.cpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/util.hpp
+ ${MBGL_ROOT}/src/mbgl/style/expression/value.cpp
+ ${MBGL_ROOT}/src/mbgl/style/filter.cpp
+ ${MBGL_ROOT}/src/mbgl/style/image.cpp
+ ${MBGL_ROOT}/src/mbgl/style/image_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/image_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layer_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/background_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/background_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/background_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/background_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/background_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/circle_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/circle_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/circle_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/circle_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/circle_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/custom_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/custom_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/custom_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_extrusion_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_extrusion_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/fill_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/heatmap_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/heatmap_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/heatmap_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/heatmap_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/heatmap_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/hillshade_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/hillshade_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/hillshade_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/hillshade_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/hillshade_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/line_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/line_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/line_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/line_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/line_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/raster_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/raster_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/raster_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/raster_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/raster_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/symbol_layer.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/symbol_layer_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/symbol_layer_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/symbol_layer_properties.cpp
+ ${MBGL_ROOT}/src/mbgl/style/layers/symbol_layer_properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/layout_property.hpp
+ ${MBGL_ROOT}/src/mbgl/style/light.cpp
+ ${MBGL_ROOT}/src/mbgl/style/light_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/light_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/light_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/style/observer.hpp
+ ${MBGL_ROOT}/src/mbgl/style/paint_property.hpp
+ ${MBGL_ROOT}/src/mbgl/style/parser.cpp
+ ${MBGL_ROOT}/src/mbgl/style/parser.hpp
+ ${MBGL_ROOT}/src/mbgl/style/properties.hpp
+ ${MBGL_ROOT}/src/mbgl/style/property_expression.cpp
+ ${MBGL_ROOT}/src/mbgl/style/rapidjson_conversion.hpp
+ ${MBGL_ROOT}/src/mbgl/style/source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/source_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/source_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/source_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/custom_geometry_source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/custom_geometry_source_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/custom_geometry_source_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/geojson_source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/geojson_source_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/geojson_source_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/image_source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/image_source_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/image_source_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/raster_dem_source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/raster_source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/raster_source_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/raster_source_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/vector_source.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/vector_source_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/sources/vector_source_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/style.cpp
+ ${MBGL_ROOT}/src/mbgl/style/style_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/style/style_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/style/types.cpp
+ ${MBGL_ROOT}/src/mbgl/text/bidi.hpp
+ ${MBGL_ROOT}/src/mbgl/text/check_max_angle.cpp
+ ${MBGL_ROOT}/src/mbgl/text/check_max_angle.hpp
+ ${MBGL_ROOT}/src/mbgl/text/collision_feature.cpp
+ ${MBGL_ROOT}/src/mbgl/text/collision_feature.hpp
+ ${MBGL_ROOT}/src/mbgl/text/collision_index.cpp
+ ${MBGL_ROOT}/src/mbgl/text/collision_index.hpp
+ ${MBGL_ROOT}/src/mbgl/text/cross_tile_symbol_index.cpp
+ ${MBGL_ROOT}/src/mbgl/text/cross_tile_symbol_index.hpp
+ ${MBGL_ROOT}/src/mbgl/text/get_anchors.cpp
+ ${MBGL_ROOT}/src/mbgl/text/get_anchors.hpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph.cpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph.hpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_atlas.cpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_atlas.hpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_manager.cpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_manager.hpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_manager_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_pbf.cpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_pbf.hpp
+ ${MBGL_ROOT}/src/mbgl/text/glyph_range.hpp
+ ${MBGL_ROOT}/src/mbgl/text/language_tag.cpp
+ ${MBGL_ROOT}/src/mbgl/text/language_tag.hpp
+ ${MBGL_ROOT}/src/mbgl/text/local_glyph_rasterizer.hpp
+ ${MBGL_ROOT}/src/mbgl/text/placement.cpp
+ ${MBGL_ROOT}/src/mbgl/text/placement.hpp
+ ${MBGL_ROOT}/src/mbgl/text/quads.cpp
+ ${MBGL_ROOT}/src/mbgl/text/quads.hpp
+ ${MBGL_ROOT}/src/mbgl/text/shaping.cpp
+ ${MBGL_ROOT}/src/mbgl/text/shaping.hpp
+ ${MBGL_ROOT}/src/mbgl/text/tagged_string.cpp
+ ${MBGL_ROOT}/src/mbgl/text/tagged_string.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/custom_geometry_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/custom_geometry_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/geojson_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/geojson_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/geojson_tile_data.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/geometry_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/geometry_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/geometry_tile_data.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/geometry_tile_data.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/geometry_tile_worker.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/geometry_tile_worker.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_dem_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_dem_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_dem_tile_worker.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_dem_tile_worker.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_tile_worker.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/raster_tile_worker.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile_cache.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile_cache.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile_id_hash.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile_id_io.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile_loader.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile_loader_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/tile_observer.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/vector_tile.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/vector_tile.hpp
+ ${MBGL_ROOT}/src/mbgl/tile/vector_tile_data.cpp
+ ${MBGL_ROOT}/src/mbgl/tile/vector_tile_data.hpp
+ ${MBGL_ROOT}/src/mbgl/util/chrono.cpp
+ ${MBGL_ROOT}/src/mbgl/util/color.cpp
+ ${MBGL_ROOT}/src/mbgl/util/compression.cpp
+ ${MBGL_ROOT}/src/mbgl/util/constants.cpp
+ ${MBGL_ROOT}/src/mbgl/util/convert.cpp
+ ${MBGL_ROOT}/src/mbgl/util/dtoa.cpp
+ ${MBGL_ROOT}/src/mbgl/util/dtoa.hpp
+ ${MBGL_ROOT}/src/mbgl/util/event.cpp
+ ${MBGL_ROOT}/src/mbgl/util/font_stack.cpp
+ ${MBGL_ROOT}/src/mbgl/util/geo.cpp
+ ${MBGL_ROOT}/src/mbgl/util/geojson_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/util/grid_index.cpp
+ ${MBGL_ROOT}/src/mbgl/util/grid_index.hpp
+ ${MBGL_ROOT}/src/mbgl/util/hash.hpp
+ ${MBGL_ROOT}/src/mbgl/util/http_header.cpp
+ ${MBGL_ROOT}/src/mbgl/util/http_header.hpp
+ ${MBGL_ROOT}/src/mbgl/util/http_timeout.cpp
+ ${MBGL_ROOT}/src/mbgl/util/http_timeout.hpp
+ ${MBGL_ROOT}/src/mbgl/util/i18n.cpp
+ ${MBGL_ROOT}/src/mbgl/util/i18n.hpp
+ ${MBGL_ROOT}/src/mbgl/util/id.cpp
+ ${MBGL_ROOT}/src/mbgl/util/id.hpp
+ ${MBGL_ROOT}/src/mbgl/util/interpolate.cpp
+ ${MBGL_ROOT}/src/mbgl/util/intersection_tests.cpp
+ ${MBGL_ROOT}/src/mbgl/util/intersection_tests.hpp
+ ${MBGL_ROOT}/src/mbgl/util/io.cpp
+ ${MBGL_ROOT}/src/mbgl/util/io.hpp
+ ${MBGL_ROOT}/src/mbgl/util/literal.hpp
+ ${MBGL_ROOT}/src/mbgl/util/logging.cpp
+ ${MBGL_ROOT}/src/mbgl/util/longest_common_subsequence.hpp
+ ${MBGL_ROOT}/src/mbgl/util/mapbox.cpp
+ ${MBGL_ROOT}/src/mbgl/util/mapbox.hpp
+ ${MBGL_ROOT}/src/mbgl/util/mat2.cpp
+ ${MBGL_ROOT}/src/mbgl/util/mat2.hpp
+ ${MBGL_ROOT}/src/mbgl/util/mat3.cpp
+ ${MBGL_ROOT}/src/mbgl/util/mat3.hpp
+ ${MBGL_ROOT}/src/mbgl/util/mat4.cpp
+ ${MBGL_ROOT}/src/mbgl/util/mat4.hpp
+ ${MBGL_ROOT}/src/mbgl/util/math.hpp
+ ${MBGL_ROOT}/src/mbgl/util/premultiply.cpp
+ ${MBGL_ROOT}/src/mbgl/util/rapidjson.cpp
+ ${MBGL_ROOT}/src/mbgl/util/rapidjson.hpp
+ ${MBGL_ROOT}/src/mbgl/util/rect.hpp
+ ${MBGL_ROOT}/src/mbgl/util/std.hpp
+ ${MBGL_ROOT}/src/mbgl/util/stopwatch.cpp
+ ${MBGL_ROOT}/src/mbgl/util/stopwatch.hpp
+ ${MBGL_ROOT}/src/mbgl/util/string.cpp
+ ${MBGL_ROOT}/src/mbgl/util/thread_local.hpp
+ ${MBGL_ROOT}/src/mbgl/util/thread_pool.cpp
+ ${MBGL_ROOT}/src/mbgl/util/thread_pool.hpp
+ ${MBGL_ROOT}/src/mbgl/util/tile_coordinate.hpp
+ ${MBGL_ROOT}/src/mbgl/util/tile_cover.cpp
+ ${MBGL_ROOT}/src/mbgl/util/tile_cover.hpp
+ ${MBGL_ROOT}/src/mbgl/util/tile_cover_impl.cpp
+ ${MBGL_ROOT}/src/mbgl/util/tile_cover_impl.hpp
+ ${MBGL_ROOT}/src/mbgl/util/tile_range.hpp
+ ${MBGL_ROOT}/src/mbgl/util/tiny_sdf.cpp
+ ${MBGL_ROOT}/src/mbgl/util/tiny_sdf.hpp
+ ${MBGL_ROOT}/src/mbgl/util/token.hpp
+ ${MBGL_ROOT}/src/mbgl/util/url.cpp
+ ${MBGL_ROOT}/src/mbgl/util/url.hpp
+ ${MBGL_ROOT}/src/mbgl/util/utf.hpp
+ ${MBGL_ROOT}/src/mbgl/util/version.cpp
+ ${MBGL_ROOT}/src/mbgl/util/version.hpp
+ ${MBGL_ROOT}/src/mbgl/util/work_request.cpp
+ ${MBGL_ROOT}/src/parsedate/parsedate.cpp
+ ${MBGL_ROOT}/src/parsedate/parsedate.hpp
+)
+
+if(EXISTS ${MBGL_ROOT}/.git/HEAD)
+ execute_process(
+ COMMAND
+ git
+ rev-parse
+ --short=8
+ HEAD
+ WORKING_DIRECTORY ${MBGL_ROOT}
+ OUTPUT_VARIABLE MBGL_VERSION_REV
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+else()
+ set(MBGL_VERSION_REV 00000000)
+endif()
+
+set_source_files_properties(
+ ${MBGL_ROOT}/src/mbgl/util/version.cpp
+ PROPERTIES
+ COMPILE_DEFINITIONS
+ MBGL_VERSION_REV="${MBGL_VERSION_REV}"
+)
+
+target_include_directories(
+ mbgl-core
+ PRIVATE ${MBGL_ROOT}/src
+)
+
+target_include_directories(
+ mbgl-core
+ PUBLIC ${MBGL_ROOT}/include
+)
+
+add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/mapbox-base/mapbox)
+add_subdirectory(${PROJECT_SOURCE_DIR}/vendor/mapbox-base/extras)
+
+include(${PROJECT_SOURCE_DIR}/vendor/boost.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/earcut.hpp.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/eternal.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/expected.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/geojson-vt-cpp.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/polylabel.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/protozero.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/shelf-pack-cpp.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/unique_resource.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/vector-tile.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/wagyu.cmake)
+
+target_link_libraries(
+ mbgl-core
+ PRIVATE
+ Mapbox::Base::Extras::kdbush.hpp
+ Mapbox::Base::supercluster.hpp
+ mbgl-vendor-boost
+ mbgl-vendor-earcut.hpp
+ mbgl-vendor-eternal
+ mbgl-vendor-geojson-vt-cpp
+ mbgl-vendor-polylabel
+ mbgl-vendor-protozero
+ mbgl-vendor-shelf-pack-cpp
+ mbgl-vendor-unique_resource
+ mbgl-vendor-vector-tile
+ mbgl-vendor-wagyu
+ PUBLIC
+ Mapbox::Base::Extras::rapidjson
+ Mapbox::Base::geojson.hpp
+ Mapbox::Base::geometry.hpp
+ Mapbox::Base::optional
+ Mapbox::Base::typewrapper
+ Mapbox::Base::variant
+ Mapbox::Base::weak
+ mbgl-vendor-expected
+)
+
+set_property(TARGET mbgl-core PROPERTY FOLDER Core)
+
+add_library(
+ Mapbox::Map ALIAS mbgl-core
+)
+
+if(MBGL_WITH_CORE_ONLY)
+ return()
+elseif(MBGL_WITH_QT)
+ include(${PROJECT_SOURCE_DIR}/platform/qt/qt.cmake)
+elseif(CMAKE_SYSTEM_NAME STREQUAL Android)
+ include(${PROJECT_SOURCE_DIR}/platform/android/android.cmake)
+elseif(CMAKE_SYSTEM_NAME STREQUAL iOS)
+ include(${PROJECT_SOURCE_DIR}/platform/ios/ios.cmake)
+elseif(CMAKE_SYSTEM_NAME STREQUAL Linux)
+ include(${PROJECT_SOURCE_DIR}/platform/linux/linux.cmake)
+elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ include(${PROJECT_SOURCE_DIR}/platform/macos/macos.cmake)
+else()
+ message(FATAL_ERROR "Unsupported target platform: " ${CMAKE_SYSTEM_NAME})
+endif()
+
+add_subdirectory(${PROJECT_SOURCE_DIR}/test)
+add_subdirectory(${PROJECT_SOURCE_DIR}/benchmark)
diff --git a/next/README.md b/next/README.md
new file mode 100644
index 0000000000..08f3a2cda8
--- /dev/null
+++ b/next/README.md
@@ -0,0 +1,42 @@
+# Mapbox GL Native
+
+A library for embedding interactive, customizable vector maps into native applications on multiple platforms. It
+takes stylesheets that conform to the [Mapbox Style Specification](https://github.com/mapbox/mapbox-gl-style-spec/),
+applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://github.com/mapbox/vector-tile-spec),
+and renders them using OpenGL. [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) is the WebGL-based counterpart,
+designed for use on the Web.
+
+| SDK | Languages | Build status |
+| --------------------------------------------------- | ---------------------------------- | ---------------------------------------- |
+| Mapbox GL Native Core | 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://codecov.io/gh/mapbox/mapbox-gl-native/branch/master/graph/badge.svg)](https://codecov.io/gh/mapbox/mapbox-gl-native) |
+| [Mapbox Maps SDK for Android](../platform/android/) | Java | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) |
+| [Mapbox Maps SDK for iOS](../platform/ios/) | Objective-C or Swift | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) |
+| [Mapbox Maps SDK for macOS](../platform/macos/) | Objective-C, Swift, or AppleScript | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) |
+| [node-mapbox-gl-native](../platform/node/) | Node.js | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) |
+| [Mapbox Maps SDK for Qt](../platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) |
+
+[Additional Mapbox GL Native–based libraries](https://wiki.openstreetmap.org/wiki/Mapbox_GL#Libraries) for **hybrid applications** are developed outside of this repository. If your platform or hybrid application framework isn’t listed there, consider embedding [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) using the standard Web capabilities on your platform.
+
+## License
+
+Mapbox GL Native is licensed under the [3-Clause BSD license](../LICENSE.md).
+
+## Developing
+
+The `next` directory contains the next generation buildsystem for Mapbox GL Native, based solely on CMake with the
+goal of minimizing the use of scripts, increase portability and support building Mapbox GL Native as a subdirectory
+of another CMake project. This new buildsystem is also designed to build offline, making use of pre-installed and
+vendorized dependencies. When using the build bot docker image, the build should produce the exact same results as
+the bots, making it a hermetically sealed build for Linux, Qt and Android.
+
+### Building and running tests
+
+The following command, executed from the root of this repository tree, will build Mapbox GL Native targeting your
+host architecture given that you have all the dependencies installed.
+
+```
+$ mkdir build && cd build
+$ cmake ../next
+$ make -j8
+$ make test ARGS=-V
+```
diff --git a/next/benchmark/CMakeLists.txt b/next/benchmark/CMakeLists.txt
new file mode 100644
index 0000000000..2ae1227d91
--- /dev/null
+++ b/next/benchmark/CMakeLists.txt
@@ -0,0 +1,41 @@
+add_library(
+ mbgl-benchmark SHARED EXCLUDE_FROM_ALL
+ ${MBGL_ROOT}/benchmark/api/query.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/api/render.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/function/camera_function.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/function/composite_function.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/function/source_function.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/parse/filter.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/parse/tile_mask.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/parse/vector_tile.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/src/mbgl/benchmark/benchmark.cpp
+ ${MBGL_ROOT}/benchmark/storage/offline_database.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/util/dtoa.benchmark.cpp
+ ${MBGL_ROOT}/benchmark/util/tilecover.benchmark.cpp
+)
+
+target_include_directories(
+ mbgl-benchmark
+ PRIVATE ${MBGL_ROOT}/benchmark/src ${MBGL_ROOT}/platform/default/include ${MBGL_ROOT}/src
+)
+
+target_include_directories(
+ mbgl-benchmark
+ PUBLIC ${MBGL_ROOT}/benchmark/include ${MBGL_ROOT}/include
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/benchmark.cmake)
+
+# Needed for testing private classes
+get_target_property(MBGL_CORE_PRIVATE_LIBRARIES mbgl-core LINK_LIBRARIES)
+
+target_link_libraries(
+ mbgl-benchmark
+ PRIVATE ${MBGL_CORE_PRIVATE_LIBRARIES} mbgl-core mbgl-vendor-benchmark
+)
+
+if(CMAKE_SYSTEM_NAME STREQUAL Android)
+ set_target_properties(mbgl-benchmark PROPERTIES LINK_FLAGS_RELEASE "-fuse-ld=gold -O2 -flto -Wl,--icf=safe")
+endif()
+
+set_property(TARGET mbgl-benchmark PROPERTY FOLDER Core)
diff --git a/next/bin/CMakeLists.txt b/next/bin/CMakeLists.txt
new file mode 100644
index 0000000000..e4222774bb
--- /dev/null
+++ b/next/bin/CMakeLists.txt
@@ -0,0 +1,26 @@
+add_executable(
+ mbgl-offline
+ ${MBGL_ROOT}/bin/offline.cpp
+)
+
+target_link_libraries(
+ mbgl-offline
+ PRIVATE Mapbox::Base::Extras::args mbgl-core
+)
+
+add_executable(
+ mbgl-render
+ ${MBGL_ROOT}/bin/render.cpp
+)
+
+target_link_libraries(
+ mbgl-render
+ PRIVATE Mapbox::Base::Extras::args mbgl-core
+)
+
+# FIXME: CI must have a valid token
+#
+# add_test(NAME mbgl-offline-tool-test COMMAND mbgl-offline -s mapbox://styles/mapbox/satellite-v9 --maxZoom=0 WORKING_DIRECTORY
+# ${MBGL_ROOT} )
+#
+# add_test(NAME mbgl-render-tool-test COMMAND mbgl-render WORKING_DIRECTORY ${MBGL_ROOT} )
diff --git a/next/expression-test/CMakeLists.txt b/next/expression-test/CMakeLists.txt
new file mode 100644
index 0000000000..1713f0b655
--- /dev/null
+++ b/next/expression-test/CMakeLists.txt
@@ -0,0 +1,36 @@
+add_executable(
+ mbgl-expression-test
+ ${MBGL_ROOT}/expression-test/expression_test_logger.cpp
+ ${MBGL_ROOT}/expression-test/expression_test_logger.hpp
+ ${MBGL_ROOT}/expression-test/expression_test_parser.cpp
+ ${MBGL_ROOT}/expression-test/expression_test_parser.hpp
+ ${MBGL_ROOT}/expression-test/expression_test_runner.cpp
+ ${MBGL_ROOT}/expression-test/expression_test_runner.hpp
+ ${MBGL_ROOT}/expression-test/main.cpp
+)
+
+target_compile_definitions(
+ mbgl-expression-test
+ PRIVATE TEST_RUNNER_ROOT_PATH="${MBGL_ROOT}"
+)
+
+# FIXME: Should not use core private interface
+target_include_directories(
+ mbgl-expression-test
+ PRIVATE ${MBGL_ROOT}/src
+)
+
+target_link_libraries(
+ mbgl-expression-test
+ PRIVATE
+ Mapbox::Base::Extras::args
+ Mapbox::Base::Extras::filesystem
+ Mapbox::Base::io
+ mbgl-core
+)
+
+set_property(TARGET mbgl-expression-test PROPERTY FOLDER Executables)
+
+string(RANDOM LENGTH 5 ALPHABET 0123456789 MBGL_EXPRESSION_TEST_SEED)
+
+add_test(NAME mbgl-expression-test COMMAND mbgl-expression-test -s --seed=${MBGL_EXPRESSION_TEST_SEED} WORKING_DIRECTORY ${MBGL_ROOT})
diff --git a/next/platform/android/android.cmake b/next/platform/android/android.cmake
new file mode 100644
index 0000000000..6b6a431ced
--- /dev/null
+++ b/next/platform/android/android.cmake
@@ -0,0 +1,306 @@
+if(NOT ANDROID_NDK_TOOLCHAIN_INCLUDED)
+ message(FATAL_ERROR "-- Toolchain file not included, see https://developer.android.com/ndk/guides/cmake")
+endif()
+
+target_compile_definitions(
+ mbgl-core
+ PUBLIC MBGL_USE_GLES2
+)
+
+target_sources(
+ mbgl-core
+ PRIVATE
+ ${MBGL_ROOT}/platform/android/src/android_renderer_backend.cpp
+ ${MBGL_ROOT}/platform/android/src/android_renderer_backend.hpp
+ ${MBGL_ROOT}/platform/android/src/android_renderer_frontend.cpp
+ ${MBGL_ROOT}/platform/android/src/android_renderer_frontend.hpp
+ ${MBGL_ROOT}/platform/android/src/annotation/marker.cpp
+ ${MBGL_ROOT}/platform/android/src/annotation/marker.hpp
+ ${MBGL_ROOT}/platform/android/src/annotation/multi_point.hpp
+ ${MBGL_ROOT}/platform/android/src/annotation/polygon.cpp
+ ${MBGL_ROOT}/platform/android/src/annotation/polygon.hpp
+ ${MBGL_ROOT}/platform/android/src/annotation/polyline.cpp
+ ${MBGL_ROOT}/platform/android/src/annotation/polyline.hpp
+ ${MBGL_ROOT}/platform/android/src/asset_manager.hpp
+ ${MBGL_ROOT}/platform/android/src/asset_manager_file_source.cpp
+ ${MBGL_ROOT}/platform/android/src/asset_manager_file_source.hpp
+ ${MBGL_ROOT}/platform/android/src/async_task.cpp
+ ${MBGL_ROOT}/platform/android/src/attach_env.cpp
+ ${MBGL_ROOT}/platform/android/src/attach_env.hpp
+ ${MBGL_ROOT}/platform/android/src/bitmap.cpp
+ ${MBGL_ROOT}/platform/android/src/bitmap.hpp
+ ${MBGL_ROOT}/platform/android/src/bitmap_factory.cpp
+ ${MBGL_ROOT}/platform/android/src/bitmap_factory.hpp
+ ${MBGL_ROOT}/platform/android/src/connectivity_listener.cpp
+ ${MBGL_ROOT}/platform/android/src/connectivity_listener.hpp
+ ${MBGL_ROOT}/platform/android/src/conversion/collection.cpp
+ ${MBGL_ROOT}/platform/android/src/conversion/collection.hpp
+ ${MBGL_ROOT}/platform/android/src/conversion/color.cpp
+ ${MBGL_ROOT}/platform/android/src/conversion/color.hpp
+ ${MBGL_ROOT}/platform/android/src/conversion/constant.cpp
+ ${MBGL_ROOT}/platform/android/src/conversion/constant.hpp
+ ${MBGL_ROOT}/platform/android/src/conversion/conversion.hpp
+ ${MBGL_ROOT}/platform/android/src/file_source.cpp
+ ${MBGL_ROOT}/platform/android/src/file_source.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/feature.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/feature.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/feature_collection.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/feature_collection.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/geometry.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/geometry.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/geometry_collection.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/geometry_collection.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/line_string.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/line_string.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/multi_line_string.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/multi_line_string.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/multi_point.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/multi_point.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/multi_polygon.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/multi_polygon.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/point.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/point.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/polygon.cpp
+ ${MBGL_ROOT}/platform/android/src/geojson/polygon.hpp
+ ${MBGL_ROOT}/platform/android/src/geojson/util.hpp
+ ${MBGL_ROOT}/platform/android/src/geometry/lat_lng.cpp
+ ${MBGL_ROOT}/platform/android/src/geometry/lat_lng.hpp
+ ${MBGL_ROOT}/platform/android/src/geometry/lat_lng_bounds.cpp
+ ${MBGL_ROOT}/platform/android/src/geometry/lat_lng_bounds.hpp
+ ${MBGL_ROOT}/platform/android/src/geometry/lat_lng_quad.cpp
+ ${MBGL_ROOT}/platform/android/src/geometry/lat_lng_quad.hpp
+ ${MBGL_ROOT}/platform/android/src/geometry/projected_meters.cpp
+ ${MBGL_ROOT}/platform/android/src/geometry/projected_meters.hpp
+ ${MBGL_ROOT}/platform/android/src/gl_functions.cpp
+ ${MBGL_ROOT}/platform/android/src/graphics/pointf.cpp
+ ${MBGL_ROOT}/platform/android/src/graphics/pointf.hpp
+ ${MBGL_ROOT}/platform/android/src/graphics/rectf.cpp
+ ${MBGL_ROOT}/platform/android/src/graphics/rectf.hpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_array.cpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_array.hpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_element.cpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_element.hpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_object.cpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_object.hpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_primitive.cpp
+ ${MBGL_ROOT}/platform/android/src/gson/json_primitive.hpp
+ ${MBGL_ROOT}/platform/android/src/http_file_source.cpp
+ ${MBGL_ROOT}/platform/android/src/image.cpp
+ ${MBGL_ROOT}/platform/android/src/java/util.cpp
+ ${MBGL_ROOT}/platform/android/src/java/util.hpp
+ ${MBGL_ROOT}/platform/android/src/java_types.cpp
+ ${MBGL_ROOT}/platform/android/src/java_types.hpp
+ ${MBGL_ROOT}/platform/android/src/jni.cpp
+ ${MBGL_ROOT}/platform/android/src/jni.hpp
+ ${MBGL_ROOT}/platform/android/src/jni_native.cpp
+ ${MBGL_ROOT}/platform/android/src/jni_native.hpp
+ ${MBGL_ROOT}/platform/android/src/logger.cpp
+ ${MBGL_ROOT}/platform/android/src/logger.hpp
+ ${MBGL_ROOT}/platform/android/src/logging_android.cpp
+ ${MBGL_ROOT}/platform/android/src/map/camera_position.cpp
+ ${MBGL_ROOT}/platform/android/src/map/camera_position.hpp
+ ${MBGL_ROOT}/platform/android/src/map/image.cpp
+ ${MBGL_ROOT}/platform/android/src/map/image.hpp
+ ${MBGL_ROOT}/platform/android/src/map_renderer.cpp
+ ${MBGL_ROOT}/platform/android/src/map_renderer.hpp
+ ${MBGL_ROOT}/platform/android/src/map_renderer_runnable.cpp
+ ${MBGL_ROOT}/platform/android/src/map_renderer_runnable.hpp
+ ${MBGL_ROOT}/platform/android/src/native_map_view.cpp
+ ${MBGL_ROOT}/platform/android/src/native_map_view.hpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_manager.cpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_manager.hpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region.cpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region.hpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region_definition.cpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region_definition.hpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region_error.cpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region_error.hpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region_status.cpp
+ ${MBGL_ROOT}/platform/android/src/offline/offline_region_status.hpp
+ ${MBGL_ROOT}/platform/android/src/run_loop.cpp
+ ${MBGL_ROOT}/platform/android/src/run_loop_impl.hpp
+ ${MBGL_ROOT}/platform/android/src/snapshotter/map_snapshot.cpp
+ ${MBGL_ROOT}/platform/android/src/snapshotter/map_snapshot.hpp
+ ${MBGL_ROOT}/platform/android/src/snapshotter/map_snapshotter.cpp
+ ${MBGL_ROOT}/platform/android/src/snapshotter/map_snapshotter.hpp
+ ${MBGL_ROOT}/platform/android/src/string_util.cpp
+ ${MBGL_ROOT}/platform/android/src/style/android_conversion.hpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/filter.cpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/filter.hpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/position.cpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/position.hpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/property_expression.hpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/property_value.hpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/transition_options.cpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/transition_options.hpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/url_or_tileset.cpp
+ ${MBGL_ROOT}/platform/android/src/style/conversion/url_or_tileset.hpp
+ ${MBGL_ROOT}/platform/android/src/style/formatted.cpp
+ ${MBGL_ROOT}/platform/android/src/style/formatted.hpp
+ ${MBGL_ROOT}/platform/android/src/style/formatted_section.cpp
+ ${MBGL_ROOT}/platform/android/src/style/formatted_section.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/background_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/background_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/circle_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/circle_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/custom_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/custom_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/fill_extrusion_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/fill_extrusion_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/fill_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/fill_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/heatmap_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/heatmap_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/hillshade_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/hillshade_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/layer_manager.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/layer_manager.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/line_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/line_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/raster_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/raster_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/symbol_layer.cpp
+ ${MBGL_ROOT}/platform/android/src/style/layers/symbol_layer.hpp
+ ${MBGL_ROOT}/platform/android/src/style/light.cpp
+ ${MBGL_ROOT}/platform/android/src/style/light.hpp
+ ${MBGL_ROOT}/platform/android/src/style/position.cpp
+ ${MBGL_ROOT}/platform/android/src/style/position.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/custom_geometry_source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/custom_geometry_source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/geojson_source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/geojson_source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/image_source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/image_source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/raster_dem_source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/raster_dem_source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/raster_source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/raster_source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/unknown_source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/unknown_source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/vector_source.cpp
+ ${MBGL_ROOT}/platform/android/src/style/sources/vector_source.hpp
+ ${MBGL_ROOT}/platform/android/src/style/transition_options.cpp
+ ${MBGL_ROOT}/platform/android/src/style/transition_options.hpp
+ ${MBGL_ROOT}/platform/android/src/style/value.cpp
+ ${MBGL_ROOT}/platform/android/src/style/value.hpp
+ ${MBGL_ROOT}/platform/android/src/text/collator.cpp
+ ${MBGL_ROOT}/platform/android/src/text/collator_jni.hpp
+ ${MBGL_ROOT}/platform/android/src/text/format_number.cpp
+ ${MBGL_ROOT}/platform/android/src/text/format_number_jni.hpp
+ ${MBGL_ROOT}/platform/android/src/text/local_glyph_rasterizer.cpp
+ ${MBGL_ROOT}/platform/android/src/text/local_glyph_rasterizer_jni.hpp
+ ${MBGL_ROOT}/platform/android/src/thread.cpp
+ ${MBGL_ROOT}/platform/android/src/timer.cpp
+ ${MBGL_ROOT}/platform/android/src/unaccent.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_frontend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gl/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/map/map_snapshotter.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/asset_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/default_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_database.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_download.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/online_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/sqlite3.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/bidi.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/png_writer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/thread_local.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/utf.cpp
+ ${MBGL_ROOT}/platform/linux/src/headless_backend_egl.cpp
+)
+
+target_include_directories(
+ mbgl-core
+ PRIVATE ${MBGL_ROOT}/platform/default/include
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/icu.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/sqlite.cmake)
+
+target_link_libraries(
+ mbgl-core
+ PRIVATE
+ EGL
+ GLESv2
+ Mapbox::Base::jni.hpp
+ android
+ atomic
+ jnigraphics
+ log
+ mbgl-vendor-icu
+ mbgl-vendor-sqlite
+ z
+)
+
+add_library(
+ mapbox-gl SHARED
+ ${MBGL_ROOT}/platform/android/src/main.cpp
+)
+
+target_link_libraries(
+ mapbox-gl
+ PRIVATE Mapbox::Base::jni.hpp mbgl-core
+)
+
+add_library(
+ example-custom-layer MODULE
+ ${MBGL_ROOT}/platform/android/src/example_custom_layer.cpp
+)
+
+target_include_directories(
+ example-custom-layer
+ PRIVATE ${MBGL_ROOT}/include
+)
+
+target_link_libraries(
+ example-custom-layer
+ PRIVATE
+ GLESv2
+ Mapbox::Base::optional
+ Mapbox::Base::typewrapper
+ Mapbox::Base::weak
+ log
+)
+
+add_executable(
+ mbgl-test-runner
+ ${MBGL_ROOT}/platform/android/src/test/runtime.cpp ${MBGL_ROOT}/platform/android/src/test/runtime.hpp
+ ${MBGL_ROOT}/platform/android/src/test/test_runner.cpp
+)
+
+target_link_libraries(
+ mbgl-test-runner
+ PRIVATE Mapbox::Base::jni.hpp mapbox-gl mbgl-test
+)
+
+add_executable(
+ mbgl-benchmark-runner
+ ${MBGL_ROOT}/platform/android/src/test/benchmark_runner.cpp ${MBGL_ROOT}/platform/android/src/test/runtime.cpp
+ ${MBGL_ROOT}/platform/android/src/test/runtime.hpp
+)
+
+target_link_libraries(
+ mbgl-benchmark-runner
+ PRIVATE Mapbox::Base::jni.hpp mapbox-gl mbgl-benchmark
+)
+
+# Android has no concept of MinSizeRel on android.toolchain.cmake and provides configurations tuned for binary size. We can push it a bit
+# more with code folding and LTO.
+set_target_properties(example-custom-layer PROPERTIES LINK_FLAGS_RELEASE "-fuse-ld=gold -O2 -flto -Wl,--icf=safe")
+set_target_properties(mapbox-gl PROPERTIES LINK_FLAGS_RELEASE "-fuse-ld=gold -O2 -flto -Wl,--icf=safe")
+set_target_properties(mbgl-benchmark-runner PROPERTIES LINK_FLAGS_RELEASE "-fuse-ld=gold -O2 -flto -Wl,--icf=safe")
+set_target_properties(mbgl-test-runner PROPERTIES LINK_FLAGS_RELEASE "-fuse-ld=gold -O2 -flto -Wl,--icf=safe")
+
+target_compile_options(example-custom-layer PRIVATE $<$<CONFIG:Release>:-Qunused-arguments -flto>)
+target_compile_options(mapbox-gl PRIVATE $<$<CONFIG:Release>:-Qunused-arguments -flto>)
+target_compile_options(mbgl-core PRIVATE $<$<CONFIG:Release>:-Qunused-arguments -flto>)
+target_compile_options(mbgl-vendor-icu PRIVATE $<$<CONFIG:Release>:-Qunused-arguments -flto>)
+target_compile_options(mbgl-vendor-sqlite PRIVATE $<$<CONFIG:Release>:-Qunused-arguments -flto>)
diff --git a/next/platform/glfw/CMakeLists.txt b/next/platform/glfw/CMakeLists.txt
new file mode 100644
index 0000000000..d4f1b9d545
--- /dev/null
+++ b/next/platform/glfw/CMakeLists.txt
@@ -0,0 +1,38 @@
+find_package(OpenGL REQUIRED)
+find_package(PkgConfig REQUIRED)
+
+pkg_search_module(GLFW glfw3 REQUIRED)
+
+add_executable(
+ mbgl-glfw
+ ${MBGL_ROOT}/platform/glfw/main.cpp
+ ${MBGL_ROOT}/platform/glfw/glfw_view.cpp
+ ${MBGL_ROOT}/platform/glfw/glfw_gl_backend.cpp
+ ${MBGL_ROOT}/platform/glfw/glfw_renderer_frontend.cpp
+ ${MBGL_ROOT}/platform/glfw/settings_json.cpp
+)
+
+target_include_directories(
+ mbgl-glfw
+ PRIVATE ${GLFW_INCLUDE_DIRS}
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/cheap-ruler-cpp.cmake)
+
+# Use target_link_directories when we move away from CMake 3.10.
+target_link_libraries(
+ mbgl-glfw
+ PRIVATE $<$<BOOL:${GLFW_LIBRARY_DIRS}>:-L${GLFW_LIBRARY_DIRS}>
+)
+
+target_link_libraries(
+ mbgl-glfw
+ PRIVATE
+ ${GLFW_LIBRARIES}
+ Mapbox::Base::Extras::args
+ Mapbox::Map
+ OpenGL::GL
+ mbgl-vendor-cheap-ruler-cpp
+)
+
+set_property(TARGET mbgl-glfw PROPERTY FOLDER Executables)
diff --git a/next/platform/ios/ios.cmake b/next/platform/ios/ios.cmake
new file mode 100644
index 0000000000..7be60bd387
--- /dev/null
+++ b/next/platform/ios/ios.cmake
@@ -0,0 +1,70 @@
+target_compile_definitions(
+ mbgl-core
+ PUBLIC MBGL_USE_GLES2 GLES_SILENCE_DEPRECATION
+)
+
+target_sources(
+ mbgl-core
+ PRIVATE
+ ${MBGL_ROOT}/platform/darwin/src/async_task.cpp
+ ${MBGL_ROOT}/platform/darwin/src/collator.mm
+ ${MBGL_ROOT}/platform/darwin/src/gl_functions.cpp
+ ${MBGL_ROOT}/platform/darwin/src/headless_backend_eagl.mm
+ ${MBGL_ROOT}/platform/darwin/src/http_file_source.mm
+ ${MBGL_ROOT}/platform/darwin/src/image.mm
+ ${MBGL_ROOT}/platform/darwin/src/local_glyph_rasterizer.mm
+ ${MBGL_ROOT}/platform/darwin/src/logging_nslog.mm
+ ${MBGL_ROOT}/platform/darwin/src/nsthread.mm
+ ${MBGL_ROOT}/platform/darwin/src/reachability.m
+ ${MBGL_ROOT}/platform/darwin/src/run_loop.cpp
+ ${MBGL_ROOT}/platform/darwin/src/string_nsstring.mm
+ ${MBGL_ROOT}/platform/darwin/src/timer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_frontend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gl/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/map/map_snapshotter.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/asset_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/default_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_database.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_download.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/online_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/sqlite3.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/bidi.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/png_writer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/thread_local.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/utf.cpp
+)
+
+target_include_directories(
+ mbgl-core
+ PRIVATE ${MBGL_ROOT}/platform/darwin/include ${MBGL_ROOT}/platform/default/include
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/icu.cmake)
+
+target_link_libraries(
+ mbgl-core
+ PRIVATE
+ "-framework CoreGraphics"
+ "-framework CoreImage"
+ "-framework CoreLocation"
+ "-framework CoreServices"
+ "-framework CoreText"
+ "-framework Foundation"
+ "-framework GLKit"
+ "-framework ImageIO"
+ "-framework OpenGLES"
+ "-framework QuartzCore"
+ "-framework Security"
+ "-framework SystemConfiguration"
+ "-framework UIKit"
+ "-framework WebKit"
+ mbgl-vendor-icu
+ sqlite3
+ z
+)
diff --git a/next/platform/linux/linux.cmake b/next/platform/linux/linux.cmake
new file mode 100644
index 0000000000..8a80da1928
--- /dev/null
+++ b/next/platform/linux/linux.cmake
@@ -0,0 +1,126 @@
+find_package(CURL REQUIRED)
+find_package(ICU REQUIRED i18n)
+find_package(ICU REQUIRED uc)
+find_package(JPEG REQUIRED)
+find_package(OpenGL REQUIRED GLX)
+find_package(PNG REQUIRED)
+find_package(PkgConfig REQUIRED)
+find_package(X11 REQUIRED)
+
+pkg_search_module(LIBUV libuv REQUIRED)
+
+target_sources(
+ mbgl-core
+ PRIVATE
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_frontend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gl/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/layermanager/layer_manager.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/asset_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/default_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/http_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_database.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_download.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/online_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/sqlite3.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/bidi.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/collator.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/local_glyph_rasterizer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/unaccent.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/async_task.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/format_number.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/image.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/jpeg_reader.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/logging_stderr.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/png_reader.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/png_writer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/run_loop.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/string_stdlib.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/thread.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/thread_local.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/timer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/utf.cpp
+ ${MBGL_ROOT}/platform/linux/src/gl_functions.cpp
+ ${MBGL_ROOT}/platform/linux/src/headless_backend_glx.cpp
+)
+
+# FIXME: Should not be needed, but now needed by node because of the headless frontend.
+target_include_directories(
+ mbgl-core
+ PUBLIC ${MBGL_ROOT}/platform/default/include
+ PRIVATE
+ ${CURL_INCLUDE_DIRS}
+ ${JPEG_INCLUDE_DIRS}
+ ${LIBUV_INCLUDE_DIRS}
+ ${X11_INCLUDE_DIRS}
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/nunicode.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/sqlite.cmake)
+
+target_link_libraries(
+ mbgl-core
+ PRIVATE
+ ${CURL_LIBRARIES}
+ ${JPEG_LIBRARIES}
+ ${LIBUV_LIBRARIES}
+ ${X11_LIBRARIES}
+ ICU::i18n
+ ICU::uc
+ OpenGL::GLX
+ PNG::PNG
+ mbgl-vendor-nunicode
+ mbgl-vendor-sqlite
+)
+
+add_custom_target(mbgl-ca-bundle)
+add_dependencies(mbgl-core mbgl-ca-bundle)
+
+add_custom_command(
+ TARGET mbgl-ca-bundle PRE_BUILD
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E
+ copy
+ ${MBGL_ROOT}/misc/ca-bundle.crt
+ ${CMAKE_BINARY_DIR}
+)
+
+add_subdirectory(${PROJECT_SOURCE_DIR}/bin)
+add_subdirectory(${PROJECT_SOURCE_DIR}/expression-test)
+add_subdirectory(${PROJECT_SOURCE_DIR}/platform/glfw)
+add_subdirectory(${PROJECT_SOURCE_DIR}/platform/node)
+add_subdirectory(${PROJECT_SOURCE_DIR}/render-test)
+
+add_executable(
+ mbgl-test-runner
+ ${MBGL_ROOT}/platform/default/src/mbgl/test/main.cpp
+)
+
+target_compile_definitions(
+ mbgl-test-runner
+ PRIVATE WORK_DIRECTORY=${MBGL_ROOT}
+)
+
+target_link_libraries(
+ mbgl-test-runner
+ PRIVATE mbgl-test
+)
+
+add_executable(
+ mbgl-benchmark-runner
+ ${MBGL_ROOT}/platform/default/src/mbgl/benchmark/main.cpp
+)
+
+target_link_libraries(
+ mbgl-benchmark-runner
+ PRIVATE mbgl-benchmark
+)
+
+add_test(NAME mbgl-benchmark-runner COMMAND mbgl-benchmark-runner WORKING_DIRECTORY ${MBGL_ROOT})
+add_test(NAME mbgl-test-runner COMMAND mbgl-test-runner WORKING_DIRECTORY ${MBGL_ROOT})
diff --git a/next/platform/macos/macos.cmake b/next/platform/macos/macos.cmake
new file mode 100644
index 0000000000..747ac5839b
--- /dev/null
+++ b/next/platform/macos/macos.cmake
@@ -0,0 +1,187 @@
+find_package(OpenGL REQUIRED)
+
+target_compile_definitions(
+ mbgl-core
+ PUBLIC GL_SILENCE_DEPRECATION
+)
+
+target_sources(
+ mbgl-core
+ PRIVATE
+ ${MBGL_ROOT}/platform/darwin/src/MGLAccountManager.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLAttributedExpression.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLAttributionInfo.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLBackgroundStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLCircleStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLClockDirectionFormatter.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLCompassDirectionFormatter.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLComputedShapeSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLCoordinateFormatter.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLDistanceFormatter.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLFeature.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLFillStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLForegroundStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLFoundation.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLGeometry.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLHeatmapStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLHillshadeStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLImageSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLLight.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLLineStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLLoggingConfiguration.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLMapCamera.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLMapSnapshotter.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLMultiPoint.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLNetworkConfiguration.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLOfflinePack.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLOfflineStorage.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLOpenGLStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLPointAnnotation.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLPointCollection.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLPolygon.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLPolyline.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLRasterDEMSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLRasterStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLRasterTileSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLRendererConfiguration.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLSDKMetricsManager.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLShape.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLShapeCollection.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLShapeOfflineRegion.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLShapeSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLStyle.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLStyleLayerManager.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLStyleValue.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLSymbolStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLTilePyramidOfflineRegion.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLTileSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLTypes.m
+ ${MBGL_ROOT}/platform/darwin/src/MGLVectorStyleLayer.mm
+ ${MBGL_ROOT}/platform/darwin/src/MGLVectorTileSource.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSArray+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSBundle+MGLAdditions.m
+ ${MBGL_ROOT}/platform/darwin/src/NSCoder+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSDate+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSDictionary+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSExpression+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSPredicate+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/NSString+MGLAdditions.m
+ ${MBGL_ROOT}/platform/darwin/src/NSURL+MGLAdditions.m
+ ${MBGL_ROOT}/platform/darwin/src/NSValue+MGLAdditions.m
+ ${MBGL_ROOT}/platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm
+ ${MBGL_ROOT}/platform/darwin/src/async_task.cpp
+ ${MBGL_ROOT}/platform/darwin/src/collator.mm
+ ${MBGL_ROOT}/platform/darwin/src/gl_functions.cpp
+ ${MBGL_ROOT}/platform/darwin/src/headless_backend_cgl.mm
+ ${MBGL_ROOT}/platform/darwin/src/http_file_source.mm
+ ${MBGL_ROOT}/platform/darwin/src/image.mm
+ ${MBGL_ROOT}/platform/darwin/src/local_glyph_rasterizer.mm
+ ${MBGL_ROOT}/platform/darwin/src/logging_nslog.mm
+ ${MBGL_ROOT}/platform/darwin/src/nsthread.mm
+ ${MBGL_ROOT}/platform/darwin/src/reachability.m
+ ${MBGL_ROOT}/platform/darwin/src/run_loop.cpp
+ ${MBGL_ROOT}/platform/darwin/src/string_nsstring.mm
+ ${MBGL_ROOT}/platform/darwin/src/timer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_frontend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gl/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/map/map_snapshotter.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/asset_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/default_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_database.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_download.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/online_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/sqlite3.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/bidi.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/png_writer.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/thread_local.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/util/utf.cpp
+ ${MBGL_ROOT}/platform/macos/src/MGLAnnotationImage.m
+ ${MBGL_ROOT}/platform/macos/src/MGLAttributionButton.mm
+ ${MBGL_ROOT}/platform/macos/src/MGLCompassCell.m
+ ${MBGL_ROOT}/platform/macos/src/MGLMapView+IBAdditions.mm
+ ${MBGL_ROOT}/platform/macos/src/MGLMapView+Impl.mm
+ ${MBGL_ROOT}/platform/macos/src/MGLMapView+OpenGL.mm
+ ${MBGL_ROOT}/platform/macos/src/MGLMapView.mm
+ ${MBGL_ROOT}/platform/macos/src/MGLOpenGLLayer.mm
+ ${MBGL_ROOT}/platform/macos/src/NSColor+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/macos/src/NSImage+MGLAdditions.mm
+ ${MBGL_ROOT}/platform/macos/src/NSProcessInfo+MGLAdditions.m
+)
+
+set_target_properties(mbgl-core PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
+
+target_compile_options(mbgl-core PRIVATE -fobjc-arc)
+
+# FIXME: Should not be needed, but now needed by node because of the headless frontend.
+target_include_directories(
+ mbgl-core
+ PUBLIC ${MBGL_ROOT}/platform/default/include
+)
+
+target_include_directories(
+ mbgl-core
+ PRIVATE ${MBGL_ROOT}/platform/darwin/include ${MBGL_ROOT}/platform/darwin/src ${MBGL_ROOT}/platform/macos/src
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/icu.cmake)
+
+target_link_libraries(
+ mbgl-core
+ PRIVATE
+ "-framework AppKit"
+ "-framework CoreGraphics"
+ "-framework CoreLocation"
+ "-framework SystemConfiguration"
+ OpenGL::GL
+ mbgl-vendor-icu
+ sqlite3
+ z
+)
+
+add_subdirectory(${PROJECT_SOURCE_DIR}/bin)
+add_subdirectory(${PROJECT_SOURCE_DIR}/expression-test)
+add_subdirectory(${PROJECT_SOURCE_DIR}/platform/glfw)
+add_subdirectory(${PROJECT_SOURCE_DIR}/platform/node)
+add_subdirectory(${PROJECT_SOURCE_DIR}/render-test)
+
+add_executable(
+ mbgl-test-runner
+ ${MBGL_ROOT}/platform/default/src/mbgl/test/main.cpp
+)
+
+target_compile_definitions(
+ mbgl-test-runner
+ PRIVATE WORK_DIRECTORY=${MBGL_ROOT}
+)
+
+target_link_libraries(
+ mbgl-test-runner
+ PRIVATE mbgl-test
+)
+
+add_executable(
+ mbgl-benchmark-runner
+ ${MBGL_ROOT}/platform/default/src/mbgl/benchmark/main.cpp
+)
+
+target_link_libraries(
+ mbgl-benchmark-runner
+ PRIVATE mbgl-benchmark
+)
+
+set_property(TARGET mbgl-benchmark-runner PROPERTY FOLDER Executables)
+set_property(TARGET mbgl-test-runner PROPERTY FOLDER Executables)
+
+add_test(NAME mbgl-benchmark-runner COMMAND mbgl-benchmark-runner WORKING_DIRECTORY ${MBGL_ROOT})
+add_test(NAME mbgl-test-runner COMMAND mbgl-test-runner WORKING_DIRECTORY ${MBGL_ROOT})
diff --git a/next/platform/node/CMakeLists.txt b/next/platform/node/CMakeLists.txt
new file mode 100644
index 0000000000..432979b473
--- /dev/null
+++ b/next/platform/node/CMakeLists.txt
@@ -0,0 +1,52 @@
+if(NOT EXISTS ${MBGL_ROOT}/node_modules/@mapbox/cmake-node-module/module.cmake)
+ message("-- Not building node bindings, dependencies not found. Run 'npm update'.")
+ return()
+endif()
+
+set(NODE_MODULE_CACHE_DIR ${CMAKE_SOURCE_DIR}/build/headers)
+include(${MBGL_ROOT}/node_modules/@mapbox/cmake-node-module/module.cmake)
+
+add_node_module(
+ mbgl-node
+ INSTALL_PATH ${MBGL_ROOT}/lib/{node_abi}/mbgl.node
+ NAN_VERSION 2.10.0
+ EXCLUDE_NODE_ABIS
+ 46
+ 47
+ 48
+ 51
+ 59
+ 67
+ 72
+)
+
+target_sources(
+ mbgl-node
+ INTERFACE
+ ${MBGL_ROOT}/platform/node/src/node_conversion.hpp
+ ${MBGL_ROOT}/platform/node/src/node_expression.cpp
+ ${MBGL_ROOT}/platform/node/src/node_expression.hpp
+ ${MBGL_ROOT}/platform/node/src/node_feature.cpp
+ ${MBGL_ROOT}/platform/node/src/node_feature.hpp
+ ${MBGL_ROOT}/platform/node/src/node_logging.cpp
+ ${MBGL_ROOT}/platform/node/src/node_logging.hpp
+ ${MBGL_ROOT}/platform/node/src/node_map.cpp
+ ${MBGL_ROOT}/platform/node/src/node_map.hpp
+ ${MBGL_ROOT}/platform/node/src/node_mapbox_gl_native.cpp
+ ${MBGL_ROOT}/platform/node/src/node_request.cpp
+ ${MBGL_ROOT}/platform/node/src/node_request.hpp
+ ${MBGL_ROOT}/platform/node/src/util/async_queue.hpp
+)
+
+target_link_libraries(
+ mbgl-node
+ INTERFACE Mapbox::Map
+)
+
+# FIXME: Node bindings only run fully on Linux now because it requires libuv RunLoop (which is the default on Linux). Also, Sanitizer will
+# not work here because node will do a dlopen(), which is not currently supported.
+if(CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT CMAKE_BUILD_TYPE STREQUAL Sanitize)
+ add_test(NAME mbgl-node-memory COMMAND npm run test-memory WORKING_DIRECTORY ${MBGL_ROOT})
+ add_test(NAME mbgl-node-query COMMAND npm run test-query WORKING_DIRECTORY ${MBGL_ROOT})
+ add_test(NAME mbgl-node-test COMMAND npm run test WORKING_DIRECTORY ${MBGL_ROOT})
+endif()
diff --git a/next/platform/qt/qt.cmake b/next/platform/qt/qt.cmake
new file mode 100644
index 0000000000..746fd39711
--- /dev/null
+++ b/next/platform/qt/qt.cmake
@@ -0,0 +1,190 @@
+# Note: Using Sqlite instead of QSqlDatabase for better compatibility.
+
+find_package(Qt5Gui REQUIRED)
+find_package(Qt5Network REQUIRED)
+find_package(Qt5OpenGL REQUIRED)
+find_package(Qt5Widgets REQUIRED)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ add_definitions("-DQT_COMPILING_QIMAGE_COMPAT_CPP")
+ add_definitions("-D_USE_MATH_DEFINES")
+ add_definitions("-Wno-deprecated-declarations")
+ add_definitions("-Wno-inconsistent-missing-override")
+ add_definitions("-Wno-macro-redefined")
+ add_definitions("-Wno-microsoft-exception-spec")
+ add_definitions("-Wno-unknown-argument")
+ add_definitions("-Wno-unknown-warning-option")
+ add_definitions("-Wno-unused-command-line-argument")
+ add_definitions("-Wno-unused-local-typedef")
+ add_definitions("-Wno-unused-private-field")
+endif()
+
+target_sources(
+ mbgl-core
+ PRIVATE
+ ${MBGL_ROOT}/platform/$<IF:$<PLATFORM_ID:Windows>,qt/src/bidi.cpp,default/src/mbgl/text/bidi.cpp>
+ ${MBGL_ROOT}/platform/default/include/mbgl/gfx/headless_backend.hpp
+ ${MBGL_ROOT}/platform/default/include/mbgl/gfx/headless_frontend.hpp
+ ${MBGL_ROOT}/platform/default/include/mbgl/gl/headless_backend.hpp
+ ${MBGL_ROOT}/platform/default/include/mbgl/text/unaccent.hpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gfx/headless_frontend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/gl/headless_backend.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/layermanager/layer_manager.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/asset_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/default_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/file_source_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_request.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/local_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_database.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/offline_download.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/online_file_source.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/storage/sqlite3.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/collator.cpp
+ ${MBGL_ROOT}/platform/default/src/mbgl/text/unaccent.cpp
+ ${MBGL_ROOT}/platform/qt/src/async_task.cpp
+ ${MBGL_ROOT}/platform/qt/src/async_task_impl.hpp
+ ${MBGL_ROOT}/platform/qt/src/format_number.cpp
+ ${MBGL_ROOT}/platform/qt/src/gl_functions.cpp
+ ${MBGL_ROOT}/platform/qt/src/headless_backend_qt.cpp
+ ${MBGL_ROOT}/platform/qt/src/http_file_source.cpp
+ ${MBGL_ROOT}/platform/qt/src/http_file_source.hpp
+ ${MBGL_ROOT}/platform/qt/src/http_request.cpp
+ ${MBGL_ROOT}/platform/qt/src/http_request.hpp
+ ${MBGL_ROOT}/platform/qt/src/local_glyph_rasterizer.cpp
+ ${MBGL_ROOT}/platform/qt/src/qt_image.cpp
+ ${MBGL_ROOT}/platform/qt/src/qt_logging.cpp
+ ${MBGL_ROOT}/platform/qt/src/run_loop.cpp
+ ${MBGL_ROOT}/platform/qt/src/run_loop_impl.hpp
+ ${MBGL_ROOT}/platform/qt/src/string_stdlib.cpp
+ ${MBGL_ROOT}/platform/qt/src/thread.cpp
+ ${MBGL_ROOT}/platform/qt/src/thread_local.cpp
+ ${MBGL_ROOT}/platform/qt/src/timer.cpp
+ ${MBGL_ROOT}/platform/qt/src/timer_impl.hpp
+ ${MBGL_ROOT}/platform/qt/src/utf.cpp
+)
+
+target_compile_definitions(
+ mbgl-core
+ PRIVATE QT_IMAGE_DECODERS
+ PUBLIC __QT__ MBGL_USE_GLES2
+)
+
+target_include_directories(
+ mbgl-core
+ PRIVATE ${MBGL_ROOT}/platform/default/include
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/icu.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/nunicode.cmake)
+include(${PROJECT_SOURCE_DIR}/vendor/sqlite.cmake)
+
+target_link_libraries(
+ mbgl-core
+ PRIVATE
+ $<$<NOT:$<PLATFORM_ID:Windows>>:z>
+ $<$<NOT:$<PLATFORM_ID:Windows>>:mbgl-vendor-icu>
+ Qt5::Core
+ Qt5::Gui
+ Qt5::Network
+ Qt5::OpenGL
+ mbgl-vendor-nunicode
+ mbgl-vendor-sqlite
+)
+
+add_library(
+ qmapboxgl SHARED
+ ${MBGL_ROOT}/platform/qt/include/qmapbox.hpp
+ ${MBGL_ROOT}/platform/qt/include/qmapboxgl.hpp
+ ${MBGL_ROOT}/platform/qt/src/qmapbox.cpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl.cpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_map_observer.cpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_map_observer.hpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_map_renderer.cpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_map_renderer.hpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_p.hpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_renderer_backend.cpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_renderer_backend.hpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_scheduler.cpp
+ ${MBGL_ROOT}/platform/qt/src/qmapboxgl_scheduler.hpp
+ ${MBGL_ROOT}/platform/qt/src/qt_conversion.hpp
+ ${MBGL_ROOT}/platform/qt/src/qt_geojson.cpp
+ ${MBGL_ROOT}/platform/qt/src/qt_geojson.hpp
+)
+
+# FIXME: Because of rapidjson conversion
+target_include_directories(
+ qmapboxgl
+ PRIVATE ${MBGL_ROOT}/src
+)
+
+target_include_directories(
+ qmapboxgl
+ PUBLIC ${MBGL_ROOT}/platform/qt/include
+)
+
+target_compile_definitions(
+ qmapboxgl
+ PRIVATE QT_BUILD_MAPBOXGL_LIB
+)
+
+target_link_libraries(
+ qmapboxgl
+ Qt5::Core
+ Qt5::Gui
+ mbgl-core
+)
+
+add_executable(
+ mbgl-qt
+ ${MBGL_ROOT}/platform/qt/app/main.cpp
+ ${MBGL_ROOT}/platform/qt/app/mapwindow.cpp
+ ${MBGL_ROOT}/platform/qt/app/mapwindow.hpp
+ ${MBGL_ROOT}/platform/qt/resources/common.qrc
+)
+
+# Qt public API should keep compatibility with old compilers for legacy systems
+set_property(TARGET mbgl-qt PROPERTY CXX_STANDARD 98)
+
+target_link_libraries(
+ mbgl-qt
+ PRIVATE Qt5::Widgets Qt5::Gui qmapboxgl
+)
+
+add_executable(
+ mbgl-test-runner
+ ${MBGL_ROOT}/platform/qt/test/main.cpp
+)
+
+target_compile_definitions(
+ mbgl-test-runner
+ PRIVATE WORK_DIRECTORY=${MBGL_ROOT}
+)
+
+target_link_libraries(
+ mbgl-test-runner
+ PRIVATE
+ Qt5::Gui
+ Qt5::OpenGL
+ mbgl-test
+ pthread
+)
+
+find_program(MBGL_QDOC NAMES qdoc)
+
+if(MBGL_QDOC)
+ add_custom_target(mbgl-qt-docs)
+
+ add_custom_command(
+ TARGET mbgl-qt-docs PRE_BUILD
+ COMMAND
+ ${MBGL_QDOC}
+ ${MBGL_ROOT}/platform/qt/config.qdocconf
+ -outputdir
+ ${CMAKE_BINARY_DIR}/docs
+ )
+endif()
+
+add_test(NAME mbgl-test-runner COMMAND mbgl-test-runner WORKING_DIRECTORY ${MBGL_ROOT})
diff --git a/next/render-test/CMakeLists.txt b/next/render-test/CMakeLists.txt
new file mode 100644
index 0000000000..6cab3fed52
--- /dev/null
+++ b/next/render-test/CMakeLists.txt
@@ -0,0 +1,50 @@
+add_executable(
+ mbgl-render-test
+ ${MBGL_ROOT}/render-test/allocation_index.cpp
+ ${MBGL_ROOT}/render-test/allocation_index.hpp
+ ${MBGL_ROOT}/render-test/filesystem.hpp
+ ${MBGL_ROOT}/render-test/main.cpp
+ ${MBGL_ROOT}/render-test/metadata.hpp
+ ${MBGL_ROOT}/render-test/parser.cpp
+ ${MBGL_ROOT}/render-test/parser.hpp
+ ${MBGL_ROOT}/render-test/runner.cpp
+ ${MBGL_ROOT}/render-test/runner.hpp
+)
+
+target_compile_definitions(
+ mbgl-render-test
+ PRIVATE TEST_RUNNER_ROOT_PATH="${MBGL_ROOT}"
+)
+
+# FIXME: Should not use core private interface
+target_include_directories(
+ mbgl-render-test
+ PRIVATE ${MBGL_ROOT}/src
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/boost.cmake)
+
+target_link_libraries(
+ mbgl-render-test
+ PRIVATE
+ Mapbox::Base::Extras::args
+ Mapbox::Base::Extras::filesystem
+ Mapbox::Base::pixelmatch-cpp
+ mbgl-core
+ mbgl-vendor-boost
+)
+
+set_property(TARGET mbgl-render-test PROPERTY FOLDER Executables)
+
+string(RANDOM LENGTH 5 ALPHABET 0123456789 MBGL_RENDER_TEST_SEED)
+
+add_test(
+ NAME mbgl-render-test
+ COMMAND
+ mbgl-render-test
+ --recycle-map
+ --shuffle
+ --seed
+ ${MBGL_RENDER_TEST_SEED}
+ WORKING_DIRECTORY ${MBGL_ROOT}
+)
diff --git a/next/test/CMakeLists.txt b/next/test/CMakeLists.txt
new file mode 100644
index 0000000000..586e2bbdaa
--- /dev/null
+++ b/next/test/CMakeLists.txt
@@ -0,0 +1,167 @@
+add_library(
+ mbgl-test SHARED EXCLUDE_FROM_ALL
+ ${MBGL_ROOT}/test/actor/actor.test.cpp
+ ${MBGL_ROOT}/test/actor/actor_ref.test.cpp
+ ${MBGL_ROOT}/test/algorithm/update_renderables.test.cpp
+ ${MBGL_ROOT}/test/algorithm/update_tile_masks.test.cpp
+ ${MBGL_ROOT}/test/api/annotations.test.cpp
+ ${MBGL_ROOT}/test/api/api_misuse.test.cpp
+ ${MBGL_ROOT}/test/api/custom_geometry_source.test.cpp
+ ${MBGL_ROOT}/test/api/custom_layer.test.cpp
+ ${MBGL_ROOT}/test/api/query.test.cpp
+ ${MBGL_ROOT}/test/api/recycle_map.cpp
+ ${MBGL_ROOT}/test/geometry/dem_data.test.cpp
+ ${MBGL_ROOT}/test/geometry/line_atlas.test.cpp
+ ${MBGL_ROOT}/test/gl/bucket.test.cpp
+ ${MBGL_ROOT}/test/gl/context.test.cpp
+ ${MBGL_ROOT}/test/gl/gl_functions.test.cpp
+ ${MBGL_ROOT}/test/gl/object.test.cpp
+ ${MBGL_ROOT}/test/map/map.test.cpp
+ ${MBGL_ROOT}/test/map/prefetch.test.cpp
+ ${MBGL_ROOT}/test/map/transform.test.cpp
+ ${MBGL_ROOT}/test/math/clamp.test.cpp
+ ${MBGL_ROOT}/test/math/minmax.test.cpp
+ ${MBGL_ROOT}/test/math/wrap.test.cpp
+ ${MBGL_ROOT}/test/programs/symbol_program.test.cpp
+ ${MBGL_ROOT}/test/renderer/backend_scope.test.cpp
+ ${MBGL_ROOT}/test/renderer/image_manager.test.cpp
+ ${MBGL_ROOT}/test/renderer/pattern_atlas.test.cpp
+ ${MBGL_ROOT}/test/sprite/sprite_loader.test.cpp
+ ${MBGL_ROOT}/test/sprite/sprite_parser.test.cpp
+ ${MBGL_ROOT}/test/src/mbgl/test/fixture_log_observer.cpp
+ ${MBGL_ROOT}/test/src/mbgl/test/getrss.cpp
+ ${MBGL_ROOT}/test/src/mbgl/test/sqlite3_test_fs.cpp
+ ${MBGL_ROOT}/test/src/mbgl/test/stub_file_source.cpp
+ ${MBGL_ROOT}/test/src/mbgl/test/test.cpp
+ ${MBGL_ROOT}/test/src/mbgl/test/util.cpp
+ ${MBGL_ROOT}/test/storage/asset_file_source.test.cpp
+ ${MBGL_ROOT}/test/storage/default_file_source.test.cpp
+ ${MBGL_ROOT}/test/storage/headers.test.cpp
+ ${MBGL_ROOT}/test/storage/http_file_source.test.cpp
+ ${MBGL_ROOT}/test/storage/local_file_source.test.cpp
+ ${MBGL_ROOT}/test/storage/offline.test.cpp
+ ${MBGL_ROOT}/test/storage/offline_database.test.cpp
+ ${MBGL_ROOT}/test/storage/offline_download.test.cpp
+ ${MBGL_ROOT}/test/storage/online_file_source.test.cpp
+ ${MBGL_ROOT}/test/storage/resource.test.cpp
+ ${MBGL_ROOT}/test/storage/sqlite.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/conversion_impl.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/function.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/geojson_options.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/layer.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/light.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/property_value.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/stringify.test.cpp
+ ${MBGL_ROOT}/test/style/conversion/tileset.test.cpp
+ ${MBGL_ROOT}/test/style/expression/expression.test.cpp
+ ${MBGL_ROOT}/test/style/expression/util.test.cpp
+ ${MBGL_ROOT}/test/style/filter.test.cpp
+ ${MBGL_ROOT}/test/style/properties.test.cpp
+ ${MBGL_ROOT}/test/style/property_expression.test.cpp
+ ${MBGL_ROOT}/test/style/source.test.cpp
+ ${MBGL_ROOT}/test/style/style.test.cpp
+ ${MBGL_ROOT}/test/style/style_image.test.cpp
+ ${MBGL_ROOT}/test/style/style_layer.test.cpp
+ ${MBGL_ROOT}/test/style/style_parser.test.cpp
+ ${MBGL_ROOT}/test/text/bidi.test.cpp
+ ${MBGL_ROOT}/test/text/cross_tile_symbol_index.test.cpp
+ ${MBGL_ROOT}/test/text/glyph_manager.test.cpp
+ ${MBGL_ROOT}/test/text/glyph_pbf.test.cpp
+ ${MBGL_ROOT}/test/text/language_tag.test.cpp
+ ${MBGL_ROOT}/test/text/local_glyph_rasterizer.test.cpp
+ ${MBGL_ROOT}/test/text/quads.test.cpp
+ ${MBGL_ROOT}/test/text/shaping.test.cpp
+ ${MBGL_ROOT}/test/text/tagged_string.test.cpp
+ ${MBGL_ROOT}/test/tile/custom_geometry_tile.test.cpp
+ ${MBGL_ROOT}/test/tile/geojson_tile.test.cpp
+ ${MBGL_ROOT}/test/tile/geometry_tile_data.test.cpp
+ ${MBGL_ROOT}/test/tile/raster_dem_tile.test.cpp
+ ${MBGL_ROOT}/test/tile/raster_tile.test.cpp
+ ${MBGL_ROOT}/test/tile/tile_coordinate.test.cpp
+ ${MBGL_ROOT}/test/tile/tile_id.test.cpp
+ ${MBGL_ROOT}/test/tile/vector_tile.test.cpp
+ ${MBGL_ROOT}/test/util/async_task.test.cpp
+ ${MBGL_ROOT}/test/util/dtoa.test.cpp
+ ${MBGL_ROOT}/test/util/geo.test.cpp
+ ${MBGL_ROOT}/test/util/grid_index.test.cpp
+ ${MBGL_ROOT}/test/util/http_timeout.test.cpp
+ ${MBGL_ROOT}/test/util/image.test.cpp
+ ${MBGL_ROOT}/test/util/mapbox.test.cpp
+ ${MBGL_ROOT}/test/util/memory.test.cpp
+ ${MBGL_ROOT}/test/util/merge_lines.test.cpp
+ ${MBGL_ROOT}/test/util/number_conversions.test.cpp
+ ${MBGL_ROOT}/test/util/offscreen_texture.test.cpp
+ ${MBGL_ROOT}/test/util/position.test.cpp
+ ${MBGL_ROOT}/test/util/projection.test.cpp
+ ${MBGL_ROOT}/test/util/run_loop.test.cpp
+ ${MBGL_ROOT}/test/util/string.test.cpp
+ ${MBGL_ROOT}/test/util/text_conversions.test.cpp
+ ${MBGL_ROOT}/test/util/thread.test.cpp
+ ${MBGL_ROOT}/test/util/thread_local.test.cpp
+ ${MBGL_ROOT}/test/util/tile_cover.test.cpp
+ ${MBGL_ROOT}/test/util/tile_range.test.cpp
+ ${MBGL_ROOT}/test/util/timer.test.cpp
+ ${MBGL_ROOT}/test/util/token.test.cpp
+ ${MBGL_ROOT}/test/util/url.test.cpp
+)
+
+find_program(MBGL_TEST_NODEJS NAMES nodejs node)
+find_program(MBGL_TEST_NPM NAMES npm)
+
+if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL Android OR NOT MBGL_TEST_NODEJS OR NOT MBGL_TEST_NPM)
+ message("Target platform does not support HTTP tests or dependencies not found.")
+
+ set(MBGL_TEST_HAS_TEST_SERVER 0)
+else()
+ set(MBGL_TEST_HAS_TEST_SERVER 1)
+endif()
+
+if(NOT DEFINED ENV{CI})
+ set(MBGL_TEST_BUILD_ON_CI 0)
+else()
+ set(MBGL_TEST_BUILD_ON_CI 1)
+endif()
+
+target_compile_definitions(
+ mbgl-test
+ PRIVATE NODE_EXECUTABLE=${MBGL_TEST_NODEJS} TEST_HAS_SERVER=${MBGL_TEST_HAS_TEST_SERVER} CI_BUILD=${MBGL_TEST_BUILD_ON_CI}
+)
+
+target_include_directories(
+ mbgl-test
+ PRIVATE
+ ${MBGL_ROOT}/platform/default/include
+ ${MBGL_ROOT}/platform/gfx/gl/src
+ ${MBGL_ROOT}/src
+ ${MBGL_ROOT}/test/src
+)
+
+target_include_directories(
+ mbgl-test
+ PUBLIC ${MBGL_ROOT}/include ${MBGL_ROOT}/test/include
+)
+
+include(${PROJECT_SOURCE_DIR}/vendor/googletest.cmake)
+
+# Needed for testing private classes
+get_target_property(MBGL_CORE_PRIVATE_LIBRARIES mbgl-core LINK_LIBRARIES)
+
+target_link_libraries(
+ mbgl-test
+ PRIVATE
+ ${MBGL_CORE_PRIVATE_LIBRARIES}
+ Mapbox::Base::Extras::args
+ Mapbox::Base::pixelmatch-cpp
+ mbgl-core
+)
+
+target_link_libraries(
+ mbgl-test
+ PUBLIC mbgl-vendor-googletest
+)
+
+if(CMAKE_SYSTEM_NAME STREQUAL Android)
+ set_target_properties(mbgl-test PROPERTIES LINK_FLAGS_RELEASE "-fuse-ld=gold -O2 -flto -Wl,--icf=safe")
+endif()
+
+set_property(TARGET mbgl-test PROPERTY FOLDER Core)
diff --git a/next/vendor b/next/vendor
new file mode 120000
index 0000000000..42a408bb6d
--- /dev/null
+++ b/next/vendor
@@ -0,0 +1 @@
+../vendor/ \ No newline at end of file
diff --git a/package.json b/package.json
index e239c60864..9b11a039cd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@mapbox/mapbox-gl-native",
- "version": "4.2.0",
+ "version": "4.3.0",
"description": "Renders map tiles with Mapbox GL",
"keywords": [
"mapbox",
@@ -14,27 +14,34 @@
"license": "BSD-2-Clause",
"dependencies": {
"@mapbox/cmake-node-module": "^1.2.0",
+ "minimatch": "^3.0.4",
"node-pre-gyp": "^0.10.2",
"npm-run-all": "^4.0.2"
},
"devDependencies": {
"@mapbox/flow-remove-types": "^1.3.0-await.upstream.2",
- "@octokit/rest": "^16.0.0",
+ "@mapbox/mvt-fixtures": "3.5.1",
"@octokit/plugin-retry": "^2.2.0",
+ "@octokit/rest": "^16.0.0",
"aws-sdk": "^2.285.1",
"csscolorparser": "~1.0.2",
+ "d3-queue": "3.0.7",
+ "diff": "4.0.1",
"ejs": "^2.5.7",
"esm": "~3.0.84",
"express": "^4.11.1",
"json-stringify-pretty-compact": "^2.0.0",
"jsonwebtoken": "^8.3.0",
"lodash": "^4.16.4",
+ "lodash.template": "4.5.0",
"mapbox-gl-styles": "2.0.2",
"pixelmatch": "^4.0.2",
"pngjs": "^3.4.0",
"pretty-bytes": "^5.1.0",
"request": "^2.88.0",
"semver": "^5.5.0",
+ "shuffle-seed": "1.1.6",
+ "st": "1.2.2",
"tape": "^4.5.1",
"xcode": "^1.0.0"
},
diff --git a/platform/android/.gitignore b/platform/android/.gitignore
index f37dc37ea6..a4c2b818fa 100644
--- a/platform/android/.gitignore
+++ b/platform/android/.gitignore
@@ -30,3 +30,5 @@ MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/acti
# Input files for running render tests
MapboxGLAndroidSDKTestApp/src/main/assets/integration/
+# Local AS configuration file
+gradle.properties \ No newline at end of file
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index 33897e7ed5..734647e4c8 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -5,8 +5,109 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
## master
### Bug fixes
+ - Fixed constant repainting for the sources with invisible layers, caused by `RenderSource::hasFadingTiles()` returning `true` all the time. [#15600](https://github.com/mapbox/mapbox-gl-native/pull/15600)
+ - Fixed an issue that caused the state of CompassView not up to date when `UiSettings.setCompassEnabled()` is set to true. [#15606](https://github.com/mapbox/mapbox-gl-native/pull/15606)
+ - Fixed an issue that `maxzoom` in style `Sources` option was ignored when URL resource is provided. It may cause problems such as extra tiles downloading at higher zoom level than `maxzoom`, or problems that wrong setting of `overscaledZ` in `OverscaledTileID` that will be passed to `SymbolLayout`, leading wrong rendering appearance. [#15581](https://github.com/mapbox/mapbox-gl-native/pull/15581)
+
+## 8.4.0-alpha.2 - September 11, 2019
+[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.4.0-alpha.1...android-v8.4.0-alpha.2) since [Mapbox Maps SDK for Android v8.4.0-alpha.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.4.0-alpha.1):
+
+### Performance improvements
+ - Newly loaded labels appear faster on the screen. [#15308](https://github.com/mapbox/mapbox-gl-native/pull/15308)
+
+### Bug fixes
+ - Fixed an issue of integer overflow when converting `tileCoordinates` to `LatLon`, which caused issues such as `queryRenderedFeatures` and `querySourceFeatures` returning incorrect coordinates at zoom levels 20 and higher. [#15560](https://github.com/mapbox/mapbox-gl-native/pull/15560)
+
+### Other Changes
+ - Bumped Java SDK dependency to 4.9.0-alpha.1 [#15570](https://github.com/mapbox/mapbox-gl-native/pull/15570)
+
+## 8.4.0-alpha.1 - September 4, 2019
+[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0...android-v8.4.0-alpha.1) since [Mapbox Maps SDK for Android v8.3.0](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0):
+
+### Features
+ - Introduce `clusterProperties` option for aggregated cluster properties. [#15425](https://github.com/mapbox/mapbox-gl-native/pull/15425)
+ - Expose the `CameraPosition#padding` field and associated utility camera position builders. This gives a choice to set a persisting map padding immediately during a transition instead of setting it lazily `MapboxMap#setPadding`, which required scheduling additional transition to be applied. This also deprecates `MapboxMap#setPadding` as there should be no need for a lazy padding setter. [#15444](https://github.com/mapbox/mapbox-gl-native/pull/15444)
+ - Add number-format expression that allows to format a number to a string, with configurations as minimal/maximal fraction and locale/currency. [#15424](https://github.com/mapbox/mapbox-gl-native/pull/15424)
+ - Enable using of `text-offset` option together with `text-variable-anchor` (if `text-radial-offset` option is not provided). If used with `text-variable-anchor`, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position. [#15542](https://github.com/mapbox/mapbox-gl-native/pull/15542)
+
+### Bug fixes
+- Fixed a rendering issue of `collisionBox` when `text-translate` or `icon-translate` is enabled. [#15467](https://github.com/mapbox/mapbox-gl-native/pull/15467)
+- Fixed unnecessary layers blink when the same components are re-added after the style reload [#15513](https://github.com/mapbox/mapbox-gl-native/pull/15513)
+- Fixed an issue in JNI marshaling code that converted a float to long that produced invalid expressions [#15557](https://github.com/mapbox/mapbox-gl-native/pull/15557)
+- Call callback onFinish() immediately when trying to move camera to current position [#15558](https://github.com/mapbox/mapbox-gl-native/pull/15558)
+
+### Performance improvements
+ - Mark used offline region resources in batches. [#15521](https://github.com/mapbox/mapbox-gl-native/pull/15521)
+
+### Other Changes
+- JNI binding refactor [#15462](https://github.com/mapbox/mapbox-gl-native/pull/15462)
+- Javadoc changes to clarify OfflineManager methods [#15519](https://github.com/mapbox/mapbox-gl-native/pull/15519)
+
+## 8.3.0 - August 28, 2019
+[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0-beta.1...android-v8.3.0) since [Mapbox Maps SDK for Android v8.3.0-beta.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0-beta.1):
+
+This release changes how offline tile requests are billed — they are now billed on a pay-as-you-go basis and all developers are able raise the offline tile limit for their users. Offline requests were previously exempt from monthly active user (MAU) billing and increasing the offline per-user tile limit to more than 6,000 tiles required the purchase of an enterprise license. By upgrading to this release, you are opting into the changes outlined in [this blog post](https://blog.mapbox.com/offline-maps-for-all-bb0fc51827be) and [#15380](https://github.com/mapbox/mapbox-gl-native/pull/15380).
+
+### Features
+ - Allow ability to pass a string array resource into `localIdeographFontFamily` for enabling/disabling the feature and specifying fallback fonts. [#15488](https://github.com/mapbox/mapbox-gl-native/pull/15488)
+
+### Bug fixes
+ - Fixed a rendering issue caused by all icons being treated as SDFs if an SDF and non-SDF icon were in the same layer. [#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456)
+ - Fixed an issue where changing location's render mode when the`LocationComponent` is disable wouldn't invalidate the foreground icon when it's back enabled. [#15507](https://github.com/mapbox/mapbox-gl-native/pull/15507)
+
+## 8.2.2 - August 23, 2019
+[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.2.1...android-v8.2.2) since [Mapbox Maps SDK for Android v8.2.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.2.1):
+
+### Bug fixes
+ - Fixed a possible crash that could be caused by invoking the wrong layer implementation casting function. [#15398](https://github.com/mapbox/mapbox-gl-native/pull/15398)
+ - Fixed use of objects after moving, potentially causing crashes. [#15408](https://github.com/mapbox/mapbox-gl-native/pull/1540)
+ - Fixed a wrong calculation of visibility in high pitch scenarios which might've led to tiles in the viewport not being rendered. [#15461](https://github.com/mapbox/mapbox-gl-native/pull/15461)
+
+### Other Changes
+ - Do not invalidate the camera immediately when map padding is set. Reverts a change introduced in the `v8.2.0`. [#15437](https://github.com/mapbox/mapbox-gl-native/pull/15437)
+
+## 8.3.0-beta.1 - August 22, 2019
+[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0-alpha.3...android-v8.3.0-beta.1) since [Mapbox Maps SDK for Android v8.3.0-alpha.3](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0-alpha.3):
+
+### Bug fixes
+ - Fixed a bug where glyphs generated through the LocalGlyphRasterizer interface were changing fonts during some zoom changes. [#15407](https://github.com/mapbox/mapbox-gl-native/pull/15407)
+ - Fixed use of objects after moving, potentially causing crashes. [#15408](https://github.com/mapbox/mapbox-gl-native/pull/15408)
+ - Fixed a possible crash that could be caused by invoking the wrong layer implementation casting function [#15398](https://github.com/mapbox/mapbox-gl-native/pull/15398).
+ - Font lookup on pre lollipop devices failed, provide default font list instead [#15410](https://github.com/mapbox/mapbox-gl-native/pull/15410).
+ - Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer [#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367).
+ - Fixed a bug where quick-scale was registered during a move gesture that followed a double-tap. Bumped gestures library to `v0.5.1`. [#15427](https://github.com/mapbox/mapbox-gl-native/pull/15427)
+ - Fixed symbol overlap when zooming out quickly. [#15416](https://github.com/mapbox/mapbox-gl-native/pull/15416)
+
+### Other Changes
+ - Do not invalidate the camera immediately when map padding is set. Reverts a change introduced in the `v8.2.0`. [#15437](https://github.com/mapbox/mapbox-gl-native/pull/15437)
+
+### Docs
+ - Add javadoc to OnStyleImageMissingListener indicating the required synchronous addition images, document workaround [#15418](https://github.com/mapbox/mapbox-gl-native/pull/15418)
+
+## 8.3.0-alpha.3 - August 15, 2019
+[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0-alpha.2...android-v8.3.0-alpha.3) since [Mapbox Maps SDK for Android v8.3.0-alpha.2](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0-alpha.2):
+
+### Gestures handling
+ - Bumped gestures library version to v0.5.0 which replaces the compat gesture detector with a custom scale gesture detector implementation. This is a softly breaking change because the underlying scale gesture detector reference has been removed. This brings a set of improvements to gestures without removing any functionality from the previous implementation. [#15136](https://github.com/mapbox/mapbox-gl-native/pull/15136)
+ - Improved scale and rotate gestures by refining sensitivity thresholds and animation velocity. [#15136](https://github.com/mapbox/mapbox-gl-native/pull/15136)
+ - Improved quick-zoom gestures by making zoom changes linear. Quick-zoom scale changes are now executed based on y-axis delta changes. [#15386](https://github.com/mapbox/mapbox-gl-native/pull/15386)
+
+### Features
+ - Introduced the text-writing-mode layout property for symbol layers. This property allows labels to be placed horizontally or vertically. The property accepts an array of enumeration values from a ( horizontal | vertical ) set. [#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)
+ - Added support for fallback fonts in `localIdeographFontFamily`. [#15255](https://github.com/mapbox/mapbox-gl-native/pull/15255)
+
+### Bug fixes
- Fixed an issue where it was possible to set the map’s content insets then tilt the map enough to see the horizon, causing performance issues [#15195](https://github.com/mapbox/mapbox-gl-native/pull/15195)
- - Allow loading of a map without a Style URI or Style JSON [#15293](https://github.com/mapbox/mapbox-gl-native/pull/15293)
+ - Allow loading of a map without a style URI or style JSON [#15293](https://github.com/mapbox/mapbox-gl-native/pull/15293)
+ - Fixed an issue where animated camera transitions zoomed in or out too dramatically [#15281](https://github.com/mapbox/mapbox-gl-native/pull/15281)
+ - Enable variable label placement when `text-allow-overlap` property is set to true [#15354](https://github.com/mapbox/mapbox-gl-native/pull/15354)
+
+## 8.3.0-alpha.2 - August 7, 2019
+[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.3.0-alpha.1...android-v8.3.0-alpha.2) since [Mapbox Maps SDK for Android v8.3.0-alpha.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.3.0-alpha.1):
+
+### Bug fixes
+ - Load a style without an URI or JSON [#15293](https://github.com/mapbox/mapbox-gl-native/pull/15293)
+ - Do not try to wake up the RunLoop if a wake is already pending. Fixes offline downloads that could freeze after resuming. [#15330](https://github.com/mapbox/mapbox-gl-native/pull/15330)
## 8.0.2 - July 31, 2019
[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.0.1...android-v8.0.2) since [Mapbox Maps SDK for Android v8.0.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.0.1):
@@ -29,7 +130,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
[Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.2.0...android-v8.3.0-alpha.1) since [Mapbox Maps SDK for Android v8.2.0](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.2.0):
### Features
- - Do not include CJK ideographs in the offline packs by default. This decreases overall offline download size but changes appereance by a default set local glyph generation [#14269](https://github.com/mapbox/mapbox-gl-native/pull/14269)
+ - Do not include CJK ideographs in the offline packs by default. This decreases overall offline download size but changes appearance by a default set local glyph generation [#14269](https://github.com/mapbox/mapbox-gl-native/pull/14269)
- Update target SDK to 28, update support library and fix gradle warnings [#15135](https://github.com/mapbox/mapbox-gl-native/pull/15135)
- Introduce VertexVector::extend() and use it in placement code [#15194](https://github.com/mapbox/mapbox-gl-native/pull/15194)
diff --git a/platform/android/LICENSE.md b/platform/android/LICENSE.md
index fdb2d43849..8423a34a6a 100644
--- a/platform/android/LICENSE.md
+++ b/platform/android/LICENSE.md
@@ -175,7 +175,7 @@ License: [The Apache Software License, Version 2.0](http://www.apache.org/licens
Mapbox GL uses portions of the Mapbox Android Gestures Library.
URL: [https://github.com/mapbox/mapbox-gestures-android](https://github.com/mapbox/mapbox-gestures-android)
-License: [BSD 2-Clause "Simplified" License](https://raw.githubusercontent.com/mapbox/mapbox-gestures-android/master/LICENSE.md)
+License: [BSD](https://opensource.org/licenses/BSD-2-Clause)
===========================================================================
@@ -185,13 +185,13 @@ License: [The Apache Software License, Version 2.0](http://www.apache.org/licens
===========================================================================
-Mapbox GL uses portions of the Mapbox Services SDK.
+Mapbox GL uses portions of the Mapbox Java SDK.
URL: [https://github.com/mapbox/mapbox-java](https://github.com/mapbox/mapbox-java)
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
===========================================================================
-Mapbox GL uses portions of the Mapbox Services SDK.
+Mapbox GL uses portions of the Mapbox Java SDK.
URL: [https://github.com/mapbox/mapbox-java](https://github.com/mapbox/mapbox-java)
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index 989a56fd63..f906453f56 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -1,4 +1,4 @@
-VERSION_NAME=8.3.0-SNAPSHOT
+VERSION_NAME=8.4.0-SNAPSHOT
# Only build native dependencies for the current ABI
# See https://code.google.com/p/android/issues/detail?id=221098#c20
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
index f44c0f1904..e2341029ff 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
@@ -5,20 +5,23 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.FloatRange;
import android.support.annotation.Keep;
-
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.Size;
+
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.utils.MathUtils;
+import java.util.Arrays;
+
/**
* Resembles the position, angle, zoom and tilt of the user's viewpoint.
*/
public final class CameraPosition implements Parcelable {
- public static final CameraPosition DEFAULT = new CameraPosition(new LatLng(), 0, 0, 0);
+ public static final CameraPosition DEFAULT = new CameraPosition(new LatLng(), 0, 0, 0, new double[] {0, 0, 0, 0});
public static final Parcelable.Creator<CameraPosition> CREATOR =
new Parcelable.Creator<CameraPosition>() {
@@ -27,7 +30,9 @@ public final class CameraPosition implements Parcelable {
LatLng target = in.readParcelable(LatLng.class.getClassLoader());
double tilt = in.readDouble();
double zoom = in.readDouble();
- return new CameraPosition(target, zoom, tilt, bearing);
+ double[] padding = new double[4];
+ in.readDoubleArray(padding);
+ return new CameraPosition(target, zoom, tilt, bearing, padding);
}
public CameraPosition[] newArray(int size) {
@@ -49,19 +54,26 @@ public final class CameraPosition implements Parcelable {
/**
* The angle, in degrees, of the camera angle from the nadir (directly facing the Earth).
- * See tilt(float) for details of restrictions on the range of values.
+ * See {@link Builder#tilt(double)} for details of restrictions on the range of values.
*/
@Keep
public final double tilt;
/**
- * Zoom level near the center of the screen. See zoom(float) for the definition of the camera's
+ * Zoom level near the center of the screen. See {@link Builder#zoom(double)} for the definition of the camera's
* zoom level.
*/
@Keep
public final double zoom;
/**
+ * Padding in pixels. Specified in left, top, right, bottom order.
+ * See {@link Builder#padding(double[])} for the definition of the camera's padding.
+ */
+ @Keep
+ public final double[] padding;
+
+ /**
* Constructs a CameraPosition.
*
* @param target The target location to align with the center of the screen.
@@ -73,13 +85,34 @@ public final class CameraPosition implements Parcelable {
* exclusive.
* @throws NullPointerException if target is null
* @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive.
+ * @deprecated use {@link CameraPosition#CameraPosition(LatLng, double, double, double, double[])} instead.
*/
- @Keep
+ @Deprecated
CameraPosition(LatLng target, double zoom, double tilt, double bearing) {
+ this(target, zoom, tilt, bearing, null);
+ }
+
+ /**
+ * Constructs a CameraPosition.
+ *
+ * @param target The target location to align with the center of the screen.
+ * @param zoom Zoom level at target. See zoom(float) for details of restrictions.
+ * @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float)
+ * for details of restrictions.
+ * @param bearing Direction that the camera is pointing in, in degrees clockwise from north.
+ * This value will be normalized to be within 0 degrees inclusive and 360 degrees
+ * exclusive.
+ * @param padding Padding in pixels. Specified in left, top, right, bottom order.
+ * @throws NullPointerException if target is null
+ * @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive.
+ */
+ @Keep
+ CameraPosition(LatLng target, double zoom, double tilt, double bearing, double[] padding) {
this.target = target;
this.bearing = bearing;
this.tilt = tilt;
this.zoom = zoom;
+ this.padding = padding;
}
/**
@@ -106,6 +139,7 @@ public final class CameraPosition implements Parcelable {
out.writeParcelable(target, flags);
out.writeDouble(tilt);
out.writeDouble(zoom);
+ out.writeDoubleArray(padding);
}
/**
@@ -115,7 +149,8 @@ public final class CameraPosition implements Parcelable {
*/
@Override
public String toString() {
- return "Target: " + target + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt;
+ return "Target: " + target + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt
+ + ", Padding:" + Arrays.toString(padding);
}
/**
@@ -145,6 +180,8 @@ public final class CameraPosition implements Parcelable {
return false;
} else if (bearing != cameraPosition.bearing) {
return false;
+ } else if (!Arrays.equals(padding, cameraPosition.padding)) {
+ return false;
}
return true;
}
@@ -159,8 +196,16 @@ public final class CameraPosition implements Parcelable {
*/
@Override
public int hashCode() {
- int result = 1;
+ int result;
+ long temp;
+ temp = Double.doubleToLongBits(bearing);
+ result = (int) (temp ^ (temp >>> 32));
result = 31 * result + (target != null ? target.hashCode() : 0);
+ temp = Double.doubleToLongBits(tilt);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(zoom);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ result = 31 * result + Arrays.hashCode(padding);
return result;
}
@@ -174,6 +219,7 @@ public final class CameraPosition implements Parcelable {
private LatLng target = null;
private double tilt = -1;
private double zoom = -1;
+ private double[] padding = null;
/**
* Create an empty builder.
@@ -194,6 +240,7 @@ public final class CameraPosition implements Parcelable {
this.target = previous.target;
this.tilt = previous.tilt;
this.zoom = previous.zoom;
+ this.padding = previous.padding;
}
}
@@ -226,6 +273,7 @@ public final class CameraPosition implements Parcelable {
target = update.getTarget();
tilt = update.getTilt();
zoom = update.getZoom();
+ padding = update.getPadding();
}
}
@@ -307,12 +355,43 @@ public final class CameraPosition implements Parcelable {
}
/**
+ * Padding in pixels that shifts the viewport by the specified amount.
+ * Applied padding is going to persist and impact following camera transformations.
+ * <p>
+ * Specified in left, top, right, bottom order.
+ * </p>
+ *
+ * @param padding Camera padding
+ * @return this
+ */
+ @NonNull
+ public Builder padding(@Size(4) double[] padding) {
+ this.padding = padding;
+ return this;
+ }
+
+ /**
+ * Padding in pixels that shifts the viewport by the specified amount.
+ * Applied padding is going to persist and impact following camera transformations.
+ * <p>
+ * Specified in left, top, right, bottom order.
+ * </p>
+ *
+ * @return this
+ */
+ @NonNull
+ public Builder padding(double left, double top, double right, double bottom) {
+ this.padding = new double[] {left, top, right, bottom};
+ return this;
+ }
+
+ /**
* Builds the CameraPosition.
*
* @return CameraPosition
*/
public CameraPosition build() {
- return new CameraPosition(target, zoom, tilt, bearing);
+ return new CameraPosition(target, zoom, tilt, bearing, padding);
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
index 16285468ee..b611456f43 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
@@ -5,6 +5,7 @@ import android.graphics.PointF;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -28,7 +29,7 @@ public final class CameraUpdateFactory {
*/
public static CameraUpdate newCameraPosition(@NonNull CameraPosition cameraPosition) {
return new CameraPositionUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt,
- cameraPosition.zoom);
+ cameraPosition.zoom, cameraPosition.padding);
}
/**
@@ -39,7 +40,7 @@ public final class CameraUpdateFactory {
* @return CameraUpdate Final Camera Position
*/
public static CameraUpdate newLatLng(@NonNull LatLng latLng) {
- return new CameraPositionUpdate(-1, latLng, -1, -1);
+ return new CameraPositionUpdate(-1, latLng, -1, -1, null);
}
/**
@@ -48,6 +49,7 @@ public final class CameraUpdateFactory {
* current camera position bearing and tilt values.
* <p>
* You can specify padding, in order to inset the bounding box from the map view's edges.
+ * The padding will not persist and impact following camera transformations.
* </p>
*
* @param bounds Bounds to match Camera position with
@@ -64,6 +66,7 @@ public final class CameraUpdateFactory {
* provided bearing and tilt values.
* <p>
* You can specify padding, in order to inset the bounding box from the map view's edges.
+ * The padding will not persist and impact following camera transformations.
* </p>
*
* @param bounds Bounds to match Camera position with
@@ -82,6 +85,7 @@ public final class CameraUpdateFactory {
* current camera position bearing and tilt values.
* <p>
* You can specify padding, in order to inset the bounding box from the map view's edges.
+ * The padding will not persist and impact following camera transformations.
* </p>
*
* @param bounds Bounds to base the Camera position out of
@@ -102,6 +106,7 @@ public final class CameraUpdateFactory {
* provided bearing and tilt values.
* <p>
* You can specify padding, in order to inset the bounding box from the map view's edges.
+ * The padding will not persist and impact following camera transformations.
* </p>
*
* @param bounds Bounds to base the Camera position out of
@@ -120,14 +125,31 @@ public final class CameraUpdateFactory {
/**
* Returns a CameraUpdate that moves the center of the screen to a latitude and longitude
- * specified by a LatLng object, and moves to the given zoom level.
+ * specified by a LatLng object taking the specified padding into account.
*
* @param latLng Target location to change to
* @param zoom Zoom level to change to
* @return CameraUpdate Final Camera Position
*/
public static CameraUpdate newLatLngZoom(@NonNull LatLng latLng, double zoom) {
- return new CameraPositionUpdate(-1, latLng, -1, zoom);
+ return new CameraPositionUpdate(-1, latLng, -1, zoom, null);
+ }
+
+ /**
+ * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude
+ * specified by a LatLng object, and moves to the given zoom level.
+ * Applied padding is going to persist and impact following camera transformations.
+ *
+ * @param latLng Target location to change to
+ * @param left Left padding
+ * @param top Top padding
+ * @param right Right padding
+ * @param bottom Bottom padding
+ * @return CameraUpdate Final Camera Position
+ */
+ public static CameraUpdate newLatLngPadding(@NonNull LatLng latLng,
+ double left, double top, double right, double bottom) {
+ return new CameraPositionUpdate(-1, latLng, -1, -1, new double[] {left, top, right, bottom});
}
/**
@@ -188,7 +210,7 @@ public final class CameraUpdateFactory {
* @return CameraUpdate Final Camera Position
*/
public static CameraUpdate bearingTo(double bearing) {
- return new CameraPositionUpdate(bearing, null, -1, -1);
+ return new CameraPositionUpdate(bearing, null, -1, -1, null);
}
/**
@@ -198,7 +220,34 @@ public final class CameraUpdateFactory {
* @return CameraUpdate Final Camera Position
*/
public static CameraUpdate tiltTo(double tilt) {
- return new CameraPositionUpdate(-1, null, tilt, -1);
+ return new CameraPositionUpdate(-1, null, tilt, -1, null);
+ }
+
+ /**
+ * Returns a CameraUpdate that when animated changes the camera padding.
+ * Applied padding is going to persist and impact following camera transformations.
+ * <p>
+ * Specified in left, top, right, bottom order.
+ * </p>
+ *
+ * @param padding Padding to change to
+ * @return CameraUpdate Final Camera Position
+ */
+ public static CameraUpdate paddingTo(double[] padding) {
+ return new CameraPositionUpdate(-1, null, -1, -1, padding);
+ }
+
+ /**
+ * Returns a CameraUpdate that when animated changes the camera padding.
+ * Applied padding is going to persist and impact following camera transformations.
+ * <p>
+ * Specified in left, top, right, bottom order.
+ * </p>
+ *
+ * @return CameraUpdate Final Camera Position
+ */
+ public static CameraUpdate paddingTo(double left, double top, double right, double bottom) {
+ return paddingTo(new double[] {left, top, right, bottom});
}
//
@@ -211,12 +260,14 @@ public final class CameraUpdateFactory {
private final LatLng target;
private final double tilt;
private final double zoom;
+ private final double[] padding;
- CameraPositionUpdate(double bearing, LatLng target, double tilt, double zoom) {
+ CameraPositionUpdate(double bearing, LatLng target, double tilt, double zoom, double[] padding) {
this.bearing = bearing;
this.target = target;
this.tilt = tilt;
this.zoom = zoom;
+ this.padding = padding;
}
public LatLng getTarget() {
@@ -235,10 +286,14 @@ public final class CameraUpdateFactory {
return zoom;
}
+ public double[] getPadding() {
+ return padding;
+ }
+
@Override
public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
- CameraPosition previousPosition = mapboxMap.getCameraPosition();
if (target == null) {
+ CameraPosition previousPosition = mapboxMap.getCameraPosition();
return new CameraPosition.Builder(this)
.target(previousPosition.target)
.build();
@@ -247,7 +302,7 @@ public final class CameraUpdateFactory {
}
@Override
- public boolean equals(@Nullable Object o) {
+ public boolean equals(Object o) {
if (this == o) {
return true;
}
@@ -266,7 +321,10 @@ public final class CameraUpdateFactory {
if (Double.compare(that.zoom, zoom) != 0) {
return false;
}
- return target != null ? target.equals(that.target) : that.target == null;
+ if (target != null ? !target.equals(that.target) : that.target != null) {
+ return false;
+ }
+ return Arrays.equals(padding, that.padding);
}
@Override
@@ -280,6 +338,7 @@ public final class CameraUpdateFactory {
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(zoom);
result = 31 * result + (int) (temp ^ (temp >>> 32));
+ result = 31 * result + Arrays.hashCode(padding);
return result;
}
@@ -290,6 +349,7 @@ public final class CameraUpdateFactory {
+ ", target=" + target
+ ", tilt=" + tilt
+ ", zoom=" + zoom
+ + ", padding=" + Arrays.toString(padding)
+ '}';
}
}
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 2c906e7203..6156b3af73 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
@@ -44,6 +44,11 @@ public class MapboxConstants {
public static final boolean DEFAULT_MANAGE_SKU_TOKEN = true;
/**
+ * Default value for font fallback for local ideograph fonts
+ */
+ public static final String DEFAULT_FONT = "sans-serif";
+
+ /**
* Unmeasured state
*/
public static final float UNMEASURED = -1f;
@@ -70,23 +75,54 @@ public class MapboxConstants {
/**
* Value by which the default rotation threshold will be increased when scaling
+ *
+ * @deprecated unused, see {@link com.mapbox.mapboxsdk.maps.UiSettings#setDisableRotateWhenScaling(boolean)}
*/
+ @Deprecated
public static final float ROTATION_THRESHOLD_INCREASE_WHEN_SCALING = 25f;
/**
- * Time within which user needs to lift fingers for velocity animation to start.
+ * Maximum absolute zoom change for multi-pointer scale velocity animation
*/
- public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L;
+ public static final double MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE = 2.5;
+
+ /**
+ * Maximum possible zoom change during the quick zoom gesture executed across the whole screen
+ */
+ public static final double QUICK_ZOOM_MAX_ZOOM_CHANGE = 4.0;
+
+ /**
+ * Scale velocity animation duration multiplier.
+ */
+ public static final double SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER = 150;
/**
* Minimum angular velocity for rotation animation
+ *
+ * @deprecated unused, see {@link #ROTATE_VELOCITY_RATIO_THRESHOLD}
*/
+ @Deprecated
public static final float MINIMUM_ANGULAR_VELOCITY = 1.5f;
/**
+ * Last scale span delta to XY velocity ratio required to execute scale velocity animation.
+ */
+ public static final double SCALE_VELOCITY_RATIO_THRESHOLD = 4 * 1e-3;
+
+ /**
+ * Last rotation delta to XY velocity ratio required to execute rotation velocity animation.
+ */
+ public static final double ROTATE_VELOCITY_RATIO_THRESHOLD = 2.2 * 1e-4;
+
+ /**
+ * Time within which user needs to lift fingers for velocity animation to start.
+ */
+ public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L;
+
+ /**
* Maximum angular velocity for rotation animation
*/
- public static final float MAXIMUM_ANGULAR_VELOCITY = 20f;
+ public static final float MAXIMUM_ANGULAR_VELOCITY = 30f;
/**
* Factor to calculate tilt change based on pixel change during shove gesture.
@@ -125,12 +161,18 @@ public class MapboxConstants {
/**
* The currently used minimum scale factor to clamp to when a quick zoom gesture occurs
+ *
+ * @deprecated unused
*/
+ @Deprecated
public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f;
/**
* The currently used maximum scale factor to clamp to when a quick zoom gesture occurs
+ *
+ * @deprecated unused
*/
+ @Deprecated
public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f;
/**
@@ -185,5 +227,6 @@ public class MapboxConstants {
public static final String STATE_ROTATE_ANIMATION_ENABLED = "mapbox_rotateAnimationEnabled";
public static final String STATE_FLING_ANIMATION_ENABLED = "mapbox_flingAnimationEnabled";
public static final String STATE_INCREASE_ROTATE_THRESHOLD = "mapbox_increaseRotateThreshold";
+ public static final String STATE_DISABLE_ROTATE_WHEN_SCALING = "mapbox_disableRotateWhenScaling";
public static final String STATE_INCREASE_SCALE_THRESHOLD = "mapbox_increaseScaleThreshold";
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java
index 706f57ce9c..1faf8f5e5e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java
@@ -10,7 +10,10 @@ public class HttpRequestUrl {
}
/**
- * Adapts a resource request url based on the host and query size.
+ * Adapts a resource request url based on the host, query size, and offline requirement.
+ * Mapbox resources downloaded for offline use are subject to separate Vector Tile and
+ * Raster Tile API pricing and are not included in the Maps SDK’s “unlimited” requests.
+ * See <a href="https://www.mapbox.com/pricing">our pricing page</a> for more information.
*
* @param host the host used as endpoint
* @param resourceUrl the resource to download
@@ -25,10 +28,11 @@ public class HttpRequestUrl {
} else {
resourceUrl = resourceUrl + "&";
}
- resourceUrl = resourceUrl + "sku=" + Mapbox.getSkuToken();
-
+ // Only add SKU token to requests not tagged as "offline" usage.
if (offline) {
- resourceUrl = resourceUrl + "&offline=true";
+ resourceUrl = resourceUrl + "offline=true";
+ } else {
+ resourceUrl = resourceUrl + "sku=" + Mapbox.getSkuToken();
}
}
return resourceUrl;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
index 5b2dcd8554..8b014b0e9c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
@@ -726,6 +726,10 @@ public final class LocationComponent {
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
+ * <p>
+ * If the camera is transitioning when the zoom change is requested, the call is going to be ignored.
+ * Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument.
+ * </p>
*
* @param zoomLevel The desired zoom level.
* @param animationDuration The zoom animation duration.
@@ -741,6 +745,10 @@ public final class LocationComponent {
"LocationComponent#zoomWhileTracking method can only be used",
" when a camera mode other than CameraMode#NONE is engaged."));
return;
+ } else if (locationCameraController.isTransitioning()) {
+ Logger.e(TAG,
+ "LocationComponent#zoomWhileTracking method call is ignored because the camera mode is transitioning");
+ return;
}
locationAnimatorCoordinator.feedNewZoomLevel(zoomLevel, mapboxMap.getCameraPosition(), animationDuration, callback);
}
@@ -751,6 +759,10 @@ public final class LocationComponent {
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
+ * <p>
+ * If the camera is transitioning when the zoom change is requested, the call is going to be ignored.
+ * Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument.
+ * </p>
*
* @param zoomLevel The desired zoom level.
* @param animationDuration The zoom animation duration.
@@ -766,6 +778,10 @@ public final class LocationComponent {
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
+ * <p>
+ * If the camera is transitioning when the zoom change is requested, the call is going to be ignored.
+ * Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument.
+ * </p>
*
* @param zoomLevel The desired zoom level.
*/
@@ -788,6 +804,10 @@ public final class LocationComponent {
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
+ * <p>
+ * If the camera is transitioning when the tilt change is requested, the call is going to be ignored.
+ * Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument.
+ * </p>
*
* @param tilt The desired camera tilt.
* @param animationDuration The tilt animation duration.
@@ -803,6 +823,10 @@ public final class LocationComponent {
"LocationComponent#tiltWhileTracking method can only be used",
" when a camera mode other than CameraMode#NONE is engaged."));
return;
+ } else if (locationCameraController.isTransitioning()) {
+ Logger.e(TAG,
+ "LocationComponent#tiltWhileTracking method call is ignored because the camera mode is transitioning");
+ return;
}
locationAnimatorCoordinator.feedNewTilt(tilt, mapboxMap.getCameraPosition(), animationDuration, callback);
}
@@ -813,6 +837,10 @@ public final class LocationComponent {
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
+ * <p>
+ * If the camera is transitioning when the tilt change is requested, the call is going to be ignored.
+ * Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument.
+ * </p>
*
* @param tilt The desired camera tilt.
* @param animationDuration The tilt animation duration.
@@ -828,6 +856,10 @@ public final class LocationComponent {
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
+ * <p>
+ * If the camera is transitioning when the tilt change is requested, the call is going to be ignored.
+ * Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument.
+ * </p>
*
* @param tilt The desired camera tilt.
*/
@@ -1182,7 +1214,6 @@ public final class LocationComponent {
}
isLayerReady = false;
- locationLayerController.hide();
staleStateManager.onStop();
if (compassEngine != null) {
updateCompassListenerState(false);
@@ -1289,6 +1320,7 @@ public final class LocationComponent {
private void disableLocationComponent() {
isEnabled = false;
+ locationLayerController.hide();
onLocationLayerStop();
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java
index 8ab03e7acd..48c89622cd 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java
@@ -1530,8 +1530,11 @@ public class LocationComponentOptions implements Parcelable {
* </p>
*
* @param padding The margins for the map in pixels (left, top, right, bottom).
+ * @deprecated Use {@link CameraPosition.Builder#padding(double, double, double, double)}
+ * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)} instead.
*/
@NonNull
+ @Deprecated
public LocationComponentOptions.Builder padding(@Nullable int[] padding) {
if (padding == null) {
throw new NullPointerException("Null padding");
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java
index b18a7c4742..54f8ee6d1a 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java
@@ -140,11 +140,11 @@ final class LocationLayerController {
}
this.renderMode = renderMode;
+ styleForeground(options);
+ determineIconsSource(options);
if (!isHidden) {
- styleForeground(options);
show();
}
- determineIconsSource(options);
internalRenderModeChangedListener.onRenderModeChanged(renderMode);
}
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 74a864a6a1..90e3934f7c 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
@@ -4,6 +4,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.PointF;
import android.os.Handler;
import android.support.annotation.NonNull;
@@ -13,7 +14,6 @@ import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import com.mapbox.android.gestures.AndroidGesturesManager;
-import com.mapbox.android.gestures.Constants;
import com.mapbox.android.gestures.MoveGestureDetector;
import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
import com.mapbox.android.gestures.RotateGestureDetector;
@@ -30,8 +30,15 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAXIMUM_ANGULAR_VELOCITY;
+import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE;
+import static com.mapbox.mapboxsdk.constants.MapboxConstants.QUICK_ZOOM_MAX_ZOOM_CHANGE;
+import static com.mapbox.mapboxsdk.constants.MapboxConstants.ROTATE_VELOCITY_RATIO_THRESHOLD;
+import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER;
+import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_RATIO_THRESHOLD;
import static com.mapbox.mapboxsdk.constants.MapboxConstants.ZOOM_RATE;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE;
+import static com.mapbox.mapboxsdk.utils.MathUtils.normalize;
/**
* Manages gestures events on a MapView.
@@ -72,6 +79,9 @@ final class MapGestureDetector {
@Nullable
private PointF constantFocalPoint;
+ @NonNull
+ private PointF doubleTapFocalPoint = new PointF();
+
private AndroidGesturesManager gesturesManager;
private Animator scaleAnimator;
@@ -113,9 +123,15 @@ final class MapGestureDetector {
com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
MoveGestureListener moveGestureListener = new MoveGestureListener();
ScaleGestureListener scaleGestureListener = new ScaleGestureListener(
- context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity));
+ context.getResources().getDimension(R.dimen.mapbox_density_constant),
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_speed),
+ context.getResources().getDimension(R.dimen.mapbox_minimum_angled_scale_speed),
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity)
+ );
RotateGestureListener rotateGestureListener = new RotateGestureListener(
context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
+ context.getResources().getDimension(R.dimen.mapbox_density_constant),
+ context.getResources().getDimension(R.dimen.mapbox_angular_velocity_multiplier),
context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity),
context.getResources().getDimension(
com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
@@ -150,6 +166,7 @@ final class MapGestureDetector {
}
gesturesManager = androidGesturesManager;
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(3f);
}
/**
@@ -256,7 +273,7 @@ final class MapGestureDetector {
};
/**
- * Schedules a velocity animator to be executed when user lift fingers,
+ * Schedules a velocity animator to be executed when user lifts fingers,
* unless canceled by the {@link #cancelAnimatorsRunnable}.
*
* @param animator animator ot be scheduled
@@ -310,8 +327,6 @@ final class MapGestureDetector {
}
private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener {
-
- private PointF doubleTapFocalPoint;
private final float doubleTapMovementThreshold;
StandardGestureListener(float doubleTapMovementThreshold) {
@@ -478,11 +493,21 @@ final class MapGestureDetector {
private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener {
+ private final float minimumGestureSpeed;
+ private final float minimumAngledGestureSpeed;
private final float minimumVelocity;
+ private final double scaleVelocityRatioThreshold;
private boolean quickZoom;
-
- ScaleGestureListener(float minimumVelocity) {
+ private float spanSinceLast;
+ private double screenHeight;
+ private double startZoom;
+
+ ScaleGestureListener(double densityMultiplier, float minimumGestureSpeed, float minimumAngledGestureSpeed,
+ float minimumVelocity) {
+ this.minimumGestureSpeed = minimumGestureSpeed;
+ this.minimumAngledGestureSpeed = minimumAngledGestureSpeed;
this.minimumVelocity = minimumVelocity;
+ this.scaleVelocityRatioThreshold = SCALE_VELOCITY_RATIO_THRESHOLD * densityMultiplier;
}
@Override
@@ -499,20 +524,45 @@ final class MapGestureDetector {
}
// re-try disabling the move detector in case double tap has been interrupted before quickzoom started
gesturesManager.getMoveGestureDetector().setEnabled(false);
+ } else {
+ if (detector.getPreviousSpan() > 0) {
+ float currSpan = detector.getCurrentSpan();
+ float prevSpan = detector.getPreviousSpan();
+ double currTime = detector.getCurrentEvent().getEventTime();
+ double prevTime = detector.getPreviousEvent().getEventTime();
+ if (currTime == prevTime) {
+ return false;
+ }
+ double speed = Math.abs(currSpan - prevSpan) / (currTime - prevTime);
+ if (speed < minimumGestureSpeed) {
+ // do not scale if the minimal gesture speed is not met
+ return false;
+ } else if (!gesturesManager.getRotateGestureDetector().isInProgress()) {
+ float rotationDeltaSinceLast = gesturesManager.getRotateGestureDetector().getDeltaSinceLast();
+ if (Math.abs(rotationDeltaSinceLast) > 0.4 && speed < minimumAngledGestureSpeed) {
+ // do not scale in case we're preferring to start rotation
+ return false;
+ }
+
+ if (uiSettings.isDisableRotateWhenScaling()) {
+ // disable rotate gesture when scale is detected first
+ gesturesManager.getRotateGestureDetector().setEnabled(false);
+ }
+ }
+ } else {
+ return false;
+ }
}
- cancelTransitionsIfRequired();
+ screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
+ startZoom = transform.getRawZoom();
- if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
- // increase rotate angle threshold when scale is detected first
- gesturesManager.getRotateGestureDetector().setAngleThreshold(
- Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
- + MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING
- );
- }
+ cancelTransitionsIfRequired();
notifyOnScaleBeginListeners(detector);
+ spanSinceLast = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan());
+
return true;
}
@@ -521,13 +571,29 @@ final class MapGestureDetector {
// dispatching camera start event only when the movement actually occurred
cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
- float scaleFactor = detector.getScaleFactor();
- double zoomBy = getNewZoom(scaleFactor, quickZoom);
PointF focalPoint = getScaleFocalPoint(detector);
- transform.zoomBy(zoomBy, focalPoint);
+ if (quickZoom) {
+ double pixelDeltaChange = Math.abs(detector.getCurrentEvent().getY() - doubleTapFocalPoint.y);
+ boolean zoomedOut = detector.getCurrentEvent().getY() < doubleTapFocalPoint.y;
+
+ // normalize the pixel delta change, ranging from 0 to screen height, to a constant zoom change range
+ double normalizedDeltaChange = normalize(pixelDeltaChange, 0, screenHeight, 0, QUICK_ZOOM_MAX_ZOOM_CHANGE);
+
+ // calculate target zoom and adjust for a multiplier
+ double targetZoom = (zoomedOut ? startZoom - normalizedDeltaChange : startZoom + normalizedDeltaChange);
+ targetZoom *= uiSettings.getZoomRate();
+
+ transform.setZoom(targetZoom, focalPoint);
+ } else {
+ double zoomBy =
+ (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) * ZOOM_RATE * uiSettings.getZoomRate();
+ transform.zoomBy(zoomBy, focalPoint);
+ }
notifyOnScaleListeners(detector);
+ spanSinceLast = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan());
+
return true;
}
@@ -536,20 +602,18 @@ final class MapGestureDetector {
if (quickZoom) {
// re-enabled the move detector if the quickzoom happened
gesturesManager.getMoveGestureDetector().setEnabled(true);
- }
-
- if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
- // resetting default angle threshold
- gesturesManager.getRotateGestureDetector().setAngleThreshold(
- Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
- );
+ } else {
+ // re-enable rotation in case it's been disabled
+ gesturesManager.getRotateGestureDetector().setEnabled(true);
}
notifyOnScaleEndListeners(detector);
float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
- if (!uiSettings.isScaleVelocityAnimationEnabled() || velocityXY < minimumVelocity) {
+ if (!uiSettings.isScaleVelocityAnimationEnabled()
+ || velocityXY < minimumVelocity
+ || spanSinceLast / velocityXY < scaleVelocityRatioThreshold) {
// notifying listeners that camera is idle only if there is no follow-up animation
dispatchCameraIdle();
return;
@@ -558,7 +622,9 @@ final class MapGestureDetector {
double zoomAddition = calculateScale(velocityXY, detector.isScalingOut());
double currentZoom = transform.getRawZoom();
PointF focalPoint = getScaleFocalPoint(detector);
- long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4);
+ // (log(x + 1 / e^2) + 2) * 150, x=0 to 2.5 (MapboxConstants#MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE)
+ long animationTime = (long) ((Math.log((Math.abs(zoomAddition)) + 1 / Math.pow(Math.E, 2)) + 2)
+ * SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER);
scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, focalPoint, animationTime);
scheduleAnimator(scaleAnimator);
}
@@ -578,36 +644,28 @@ final class MapGestureDetector {
}
private double calculateScale(double velocityXY, boolean isScalingOut) {
- double zoomAddition = (float) Math.log(velocityXY / 1000 + 1);
+ double zoomAddition = velocityXY * MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE * 1e-4;
+ zoomAddition = MathUtils.clamp(zoomAddition, 0, MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE);
if (isScalingOut) {
zoomAddition = -zoomAddition;
}
return zoomAddition;
}
-
- private double getNewZoom(float scaleFactor, boolean quickZoom) {
- double zoomBy = (Math.log(scaleFactor) / Math.log(Math.PI / 2)) * ZOOM_RATE * uiSettings.getZoomRate();
- if (quickZoom) {
- // clamp scale factors we feed to core #7514
- boolean negative = zoomBy < 0;
- zoomBy = MathUtils.clamp(Math.abs(zoomBy),
- MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP,
- MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP);
- return negative ? -zoomBy : zoomBy;
- }
- return zoomBy;
- }
}
private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
private final float minimumScaleSpanWhenRotating;
+ private final float angularVelocityMultiplier;
private final float minimumAngularVelocity;
+ private final double rotateVelocityRatioThreshold;
private final float defaultSpanSinceStartThreshold;
- RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity,
- float defaultSpanSinceStartThreshold) {
+ RotateGestureListener(float minimumScaleSpanWhenRotating, double densityMultiplier, float angularVelocityMultiplier,
+ float minimumAngularVelocity, float defaultSpanSinceStartThreshold) {
this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
+ this.angularVelocityMultiplier = angularVelocityMultiplier;
this.minimumAngularVelocity = minimumAngularVelocity;
+ this.rotateVelocityRatioThreshold = ROTATE_VELOCITY_RATIO_THRESHOLD * densityMultiplier;
this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold;
}
@@ -617,7 +675,23 @@ final class MapGestureDetector {
return false;
}
- cancelTransitionsIfRequired();
+ float deltaSinceLast = Math.abs(detector.getDeltaSinceLast());
+ double currTime = detector.getCurrentEvent().getEventTime();
+ double prevTime = detector.getPreviousEvent().getEventTime();
+ if (currTime == prevTime) {
+ return false;
+ }
+ double speed = deltaSinceLast / (currTime - prevTime);
+ float deltaSinceStart = Math.abs(detector.getDeltaSinceStart());
+
+ // adjust the responsiveness of a rotation gesture - the higher the speed, the bigger the threshold
+ if (speed < 0.04
+ || (speed > 0.07 && deltaSinceStart < 5)
+ || (speed > 0.15 && deltaSinceStart < 7)
+ || (speed > 0.5 && deltaSinceStart < 15)
+ ) {
+ return false;
+ }
if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
// when rotation starts, interrupting scale and increasing the threshold
@@ -626,6 +700,8 @@ final class MapGestureDetector {
gesturesManager.getStandardScaleGestureDetector().interrupt();
}
+ cancelTransitionsIfRequired();
+
notifyOnRotateBeginListeners(detector);
return true;
@@ -659,22 +735,23 @@ final class MapGestureDetector {
notifyOnRotateEndListeners(detector);
- if (!uiSettings.isRotateVelocityAnimationEnabled() || Math.abs(angularVelocity) < minimumAngularVelocity) {
+ angularVelocity = angularVelocity * angularVelocityMultiplier;
+ angularVelocity = MathUtils.clamp(angularVelocity, -MAXIMUM_ANGULAR_VELOCITY, MAXIMUM_ANGULAR_VELOCITY);
+
+ float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
+ float delta = Math.abs(detector.getDeltaSinceLast());
+ double ratio = delta / velocityXY;
+
+ if (!uiSettings.isRotateVelocityAnimationEnabled()
+ || Math.abs(angularVelocity) < minimumAngularVelocity
+ || (gesturesManager.getStandardScaleGestureDetector().isInProgress() && ratio < rotateVelocityRatioThreshold)) {
// notifying listeners that camera is idle only if there is no follow-up animation
dispatchCameraIdle();
return;
}
- boolean negative = angularVelocity < 0;
- angularVelocity = (float) Math.pow(angularVelocity, 2);
- angularVelocity = MathUtils.clamp(
- angularVelocity, MapboxConstants.MINIMUM_ANGULAR_VELOCITY, MapboxConstants.MAXIMUM_ANGULAR_VELOCITY);
-
- long animationTime = (long) (Math.log(angularVelocity + 1) * 500);
-
- if (negative) {
- angularVelocity = -angularVelocity;
- }
+ long animationTime = (long) ((Math.log((Math.abs(angularVelocity)) + 1 / Math.pow(Math.E, 2)) + 2)
+ * SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER);
PointF focalPoint = getRotateFocalPoint(detector);
rotateAnimator = createRotateAnimator(angularVelocity, animationTime, focalPoint);
@@ -901,10 +978,6 @@ final class MapGestureDetector {
&& (!uiSettings.isTiltGesturesEnabled() || !gesturesManager.getShoveGestureDetector().isInProgress());
}
- private boolean isZoomValid(double mapZoom) {
- return mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM;
- }
-
void notifyOnMapClickListeners(@NonNull PointF tapPoint) {
for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) {
if (listener.onMapClick(projection.fromScreenLocation(tapPoint))) {
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 1367de8729..4521d2ae60 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
@@ -1013,14 +1013,18 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
}
/**
- * Interface definition for a callback to be invoked with the id of a missing icon.
+ * Interface definition for a callback to be invoked with the id of a missing icon. The icon should be added
+ * synchronously with {@link Style#addImage(String, Bitmap)} to be rendered on the current zoom level. When loading
+ * icons asynchronously, you can load a placeholder image and replace it when you icon has loaded.
* <p>
* {@link MapView#addOnStyleImageMissingListener(OnStyleImageMissingListener)}
* </p>
*/
public interface OnStyleImageMissingListener {
/**
- * Called when the map is missing an icon.
+ * Called when the map is missing an icon.The icon should be added synchronously with
+ * {@link Style#addImage(String, Bitmap)} to be rendered on the current zoom level. When loading icons
+ * asynchronously, you can load a placeholder image and replace it when you icon has loaded.
*
* @param id the id of the icon that is missing
*/
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index 8e1019b379..acd5093dad 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -1540,24 +1540,38 @@ public final class MapboxMap {
* frame from the viewport. For instance, if the only the top edge is inset, the
* map center is effectively shifted downward.
* </p>
+ * <p>
+ * This method sets the padding "lazily".
+ * This means that the <b>padding is going to be applied with the next camera transformation.</b>
+ * To apply the padding immediately use {@link CameraPosition.Builder#padding(double, double, double, double)}
+ * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)}.
+ * </p>
*
* @param left The left margin in pixels.
* @param top The top margin in pixels.
* @param right The right margin in pixels.
* @param bottom The bottom margin in pixels.
+ * @deprecated Use {@link CameraPosition.Builder#padding(double, double, double, double)}
+ * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)} instead.
*/
+ @Deprecated
public void setPadding(int left, int top, int right, int bottom) {
+ // TODO padding should be passed as doubles
projection.setContentPadding(new int[] {left, top, right, bottom});
uiSettings.invalidate();
}
/**
- * Returns the current configured content padding on map view.
+ * Returns the current configured content padding on map view. This might return the currently visible padding
+ * or the padding cached but not yet applied by {@link #setPadding(int, int, int, int)}.
*
* @return An array with length 4 in the LTRB order.
+ * @deprecated Use {@link CameraPosition#padding} instead.
*/
+ @Deprecated
@NonNull
public int[] getPadding() {
+ // TODO this should return double[] (semver major change)
return projection.getContentPadding();
}
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 a394b97124..6cd3271d12 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
@@ -10,6 +10,7 @@ import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.content.res.ResourcesCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -19,6 +20,7 @@ import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
+import com.mapbox.mapboxsdk.utils.FontUtils;
import java.util.Arrays;
@@ -69,7 +71,10 @@ public class MapboxMapOptions implements Parcelable {
private boolean prefetchesTiles = true;
private boolean zMediaOverlay = false;
+
+ private boolean localIdeographFontFamilyEnabled = true;
private String localIdeographFontFamily;
+ private String[] localIdeographFontFamilies;
private String apiBaseUri;
@@ -130,7 +135,9 @@ public class MapboxMapOptions implements Parcelable {
translucentTextureSurface = in.readByte() != 0;
prefetchesTiles = in.readByte() != 0;
zMediaOverlay = in.readByte() != 0;
+ localIdeographFontFamilyEnabled = in.readByte() != 0;
localIdeographFontFamily = in.readString();
+ localIdeographFontFamilies = in.createStringArray();
pixelRatio = in.readFloat();
foregroundLoadColor = in.readInt();
crossSourceCollisions = in.readByte() != 0;
@@ -156,9 +163,15 @@ public class MapboxMapOptions implements Parcelable {
*/
@NonNull
public static MapboxMapOptions createFromAttributes(@NonNull Context context, @Nullable AttributeSet attrs) {
- MapboxMapOptions mapboxMapOptions = new MapboxMapOptions();
- float pxlRatio = context.getResources().getDisplayMetrics().density;
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.mapbox_MapView, 0, 0);
+ return createFromAttributes(new MapboxMapOptions(), context, typedArray);
+ }
+
+ @VisibleForTesting
+ static MapboxMapOptions createFromAttributes(@NonNull MapboxMapOptions mapboxMapOptions,
+ @NonNull Context context,
+ @Nullable TypedArray typedArray) {
+ float pxlRatio = context.getResources().getDisplayMetrics().density;
try {
mapboxMapOptions.camera(new CameraPosition.Builder(typedArray).build());
@@ -247,12 +260,24 @@ public class MapboxMapOptions implements Parcelable {
mapboxMapOptions.renderSurfaceOnTop(
typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableZMediaOverlay, false));
- String localIdeographFontFamily =
- typedArray.getString(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamily);
- if (localIdeographFontFamily == null) {
- localIdeographFontFamily = "sans-serif";
+ mapboxMapOptions.localIdeographFontFamilyEnabled =
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_localIdeographEnabled, true);
+
+ int localIdeographFontFamiliesResId =
+ typedArray.getResourceId(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamilies, 0);
+ if (localIdeographFontFamiliesResId != 0) {
+ String[] localIdeographFontFamilies =
+ context.getResources().getStringArray(localIdeographFontFamiliesResId);
+ mapboxMapOptions.localIdeographFontFamily(localIdeographFontFamilies);
+ } else {
+ // did user provide xml font string?
+ String localIdeographFontFamily =
+ typedArray.getString(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamily);
+ if (localIdeographFontFamily == null) {
+ localIdeographFontFamily = MapboxConstants.DEFAULT_FONT;
+ }
+ mapboxMapOptions.localIdeographFontFamily(localIdeographFontFamily);
}
- mapboxMapOptions.localIdeographFontFamily(localIdeographFontFamily);
mapboxMapOptions.pixelRatio(
typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_pixelRatio, 0));
@@ -632,19 +657,50 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * Enable local ideograph font family, defaults to true.
+ *
+ * @param enabled true to enable, false to disable
+ * @return This
+ */
+ @NonNull
+ public MapboxMapOptions localIdeographFontFamilyEnabled(boolean enabled) {
+ this.localIdeographFontFamilyEnabled = enabled;
+ return this;
+ }
+
+ /**
* Set the font family for generating glyphs locally for ideographs in the &#x27;CJK Unified Ideographs&#x27;
* and &#x27;Hangul Syllables&#x27; ranges.
* <p>
* The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}.
* Default system fonts are defined in &#x27;/system/etc/fonts.xml&#x27;
- * Default font for local ideograph font family is "sans-serif".
+ * Default font for local ideograph font family is {@link MapboxConstants#DEFAULT_FONT}.
*
* @param fontFamily font family for local ideograph generation.
* @return This
*/
@NonNull
public MapboxMapOptions localIdeographFontFamily(String fontFamily) {
- this.localIdeographFontFamily = fontFamily;
+ this.localIdeographFontFamily = FontUtils.extractValidFont(fontFamily);
+ return this;
+ }
+
+ /**
+ * Set a font family from range of font families for generating glyphs locally for ideographs in the
+ * &#x27;CJK Unified Ideographs&#x27; and &#x27;Hangul Syllables&#x27; ranges. The first matching font
+ * will be selected. If no valid font found, it defaults to {@link MapboxConstants#DEFAULT_FONT}.
+ * <p>
+ * The font families are checked against the default system fonts defined in
+ * &#x27;/system/etc/fonts.xml&#x27; Default font for local ideograph font family is
+ * {@link MapboxConstants#DEFAULT_FONT}.
+ * </p>
+ *
+ * @param fontFamilies an array of font families for local ideograph generation.
+ * @return This
+ */
+ @NonNull
+ public MapboxMapOptions localIdeographFontFamily(String... fontFamilies) {
+ this.localIdeographFontFamily = FontUtils.extractValidFont(fontFamilies);
return this;
}
@@ -925,6 +981,11 @@ public class MapboxMapOptions implements Parcelable {
return textureMode;
}
+ /**
+ * Returns true if TextureView supports a translucent surface
+ *
+ * @return True if translucent surface is active
+ */
public boolean getTranslucentTextureSurface() {
return translucentTextureSurface;
}
@@ -942,12 +1003,23 @@ public class MapboxMapOptions implements Parcelable {
/**
* Returns the font-family for locally overriding generation of glyphs in the
* &#x27;CJK Unified Ideographs&#x27; and &#x27;Hangul Syllables&#x27; ranges.
- * Default font for local ideograph font family is "sans-serif".
+ * Default font for local ideograph font family is {@link MapboxConstants#DEFAULT_FONT}.
+ * Returns null if local ideograph font families are disabled.
*
* @return Local ideograph font family name.
*/
+ @Nullable
public String getLocalIdeographFontFamily() {
- return localIdeographFontFamily;
+ return localIdeographFontFamilyEnabled ? localIdeographFontFamily : null;
+ }
+
+ /**
+ * Returns true if local ideograph font family is enabled, defaults to true.
+ *
+ * @return True if local ideograph font family is enabled
+ */
+ public boolean isLocalIdeographFontFamilyEnabled() {
+ return localIdeographFontFamilyEnabled;
}
/**
@@ -1010,7 +1082,9 @@ public class MapboxMapOptions implements Parcelable {
dest.writeByte((byte) (translucentTextureSurface ? 1 : 0));
dest.writeByte((byte) (prefetchesTiles ? 1 : 0));
dest.writeByte((byte) (zMediaOverlay ? 1 : 0));
+ dest.writeByte((byte) (localIdeographFontFamilyEnabled ? 1 : 0));
dest.writeString(localIdeographFontFamily);
+ dest.writeStringArray(localIdeographFontFamilies);
dest.writeFloat(pixelRatio);
dest.writeInt(foregroundLoadColor);
dest.writeByte((byte) (crossSourceCollisions ? 1 : 0));
@@ -1095,7 +1169,6 @@ public class MapboxMapOptions implements Parcelable {
if (!Arrays.equals(attributionMargins, options.attributionMargins)) {
return false;
}
-
if (apiBaseUri != null ? !apiBaseUri.equals(options.apiBaseUri) : options.apiBaseUri != null) {
return false;
}
@@ -1105,9 +1178,16 @@ public class MapboxMapOptions implements Parcelable {
if (zMediaOverlay != options.zMediaOverlay) {
return false;
}
+ if (localIdeographFontFamilyEnabled != options.localIdeographFontFamilyEnabled) {
+ return false;
+ }
if (!localIdeographFontFamily.equals(options.localIdeographFontFamily)) {
return false;
}
+ if (!Arrays.equals(localIdeographFontFamilies, options.localIdeographFontFamilies)) {
+ return false;
+ }
+
if (pixelRatio != options.pixelRatio) {
return false;
}
@@ -1152,7 +1232,9 @@ public class MapboxMapOptions implements Parcelable {
result = 31 * result + (translucentTextureSurface ? 1 : 0);
result = 31 * result + (prefetchesTiles ? 1 : 0);
result = 31 * result + (zMediaOverlay ? 1 : 0);
+ result = 31 * result + (localIdeographFontFamilyEnabled ? 1 : 0);
result = 31 * result + (localIdeographFontFamily != null ? localIdeographFontFamily.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(localIdeographFontFamilies);
result = 31 * result + (int) pixelRatio;
result = 31 * result + (crossSourceCollisions ? 1 : 0);
return result;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java
index da70013713..7f3017c7ae 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java
@@ -41,12 +41,12 @@ interface NativeMap {
// Camera API
//
- void jumpTo(@NonNull LatLng center, double zoom, double pitch, double bearing);
+ void jumpTo(@NonNull LatLng center, double zoom, double pitch, double bearing, double[] padding);
- void easeTo(@NonNull LatLng center, double zoom, double bearing, double pitch, long duration,
+ void easeTo(@NonNull LatLng center, double zoom, double bearing, double pitch, double[] padding, long duration,
boolean easingInterpolator);
- void flyTo(@NonNull LatLng center, double zoom, double bearing, double pitch, long duration);
+ void flyTo(@NonNull LatLng center, double zoom, double bearing, double pitch, double[] padding, long duration);
void moveBy(double deltaX, double deltaY, long duration);
@@ -161,9 +161,9 @@ interface NativeMap {
// Content padding API
//
- void setContentPadding(float[] padding);
+ void setContentPadding(double[] padding);
- float[] getContentPadding();
+ double[] getContentPadding();
//
// Query API
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 fc68a408fa..8496160c7e 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
@@ -10,6 +10,7 @@ import android.support.annotation.Keep;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.LibraryLoader;
@@ -68,6 +69,10 @@ final class NativeMapView implements NativeMap {
// Flag to indicate destroy was called
private boolean destroyed = false;
+ // Cached to enable lazily set padding.
+ // Whenever an animation is schedule, this value is cleared and the source of truth becomes the core transform state.
+ private double[] edgeInsets;
+
// Holds the pointer to JNI NativeMapView
@Keep
private long nativePtr = 0;
@@ -241,7 +246,8 @@ final class NativeMapView implements NativeMap {
if (checkState("setLatLng")) {
return;
}
- nativeSetLatLng(latLng.getLatitude(), latLng.getLongitude(), duration);
+ nativeSetLatLng(latLng.getLatitude(), latLng.getLongitude(),
+ getAnimationPaddingAndClearCachedInsets(null), duration);
}
@Override
@@ -374,30 +380,20 @@ final class NativeMapView implements NativeMap {
}
@Override
- public void setContentPadding(float[] padding) {
+ public void setContentPadding(double[] padding) {
if (checkState("setContentPadding")) {
return;
}
- // TopLeftBottomRight
- nativeSetContentPadding(
- padding[1] / pixelRatio,
- padding[0] / pixelRatio,
- padding[3] / pixelRatio,
- padding[2] / pixelRatio);
+ this.edgeInsets = padding;
}
@Override
- public float[] getContentPadding() {
+ public double[] getContentPadding() {
if (checkState("getContentPadding")) {
- return new float[] {0, 0, 0, 0};
+ return new double[] {0, 0, 0, 0};
}
- float[] topLeftBottomRight = nativeGetContentPadding();
- return new float[] {
- topLeftBottomRight[1] * pixelRatio,
- topLeftBottomRight[0] * pixelRatio,
- topLeftBottomRight[3] * pixelRatio,
- topLeftBottomRight[2] * pixelRatio
- };
+ // if cached insets are not applied yet, return them, otherwise, get the padding from the camera
+ return edgeInsets != null ? edgeInsets : getCameraPosition().padding;
}
@Override
@@ -672,29 +668,31 @@ final class NativeMapView implements NativeMap {
}
@Override
- public void jumpTo(@NonNull LatLng center, double zoom, double pitch, double angle) {
+ public void jumpTo(@NonNull LatLng center, double zoom, double pitch, double angle, double[] padding) {
if (checkState("jumpTo")) {
return;
}
- nativeJumpTo(angle, center.getLatitude(), center.getLongitude(), pitch, zoom);
+ nativeJumpTo(angle, center.getLatitude(), center.getLongitude(), pitch, zoom,
+ getAnimationPaddingAndClearCachedInsets(padding));
}
@Override
- public void easeTo(@NonNull LatLng center, double zoom, double angle, double pitch, long duration,
+ public void easeTo(@NonNull LatLng center, double zoom, double angle, double pitch, double[] padding, long duration,
boolean easingInterpolator) {
if (checkState("easeTo")) {
return;
}
nativeEaseTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom,
- easingInterpolator);
+ getAnimationPaddingAndClearCachedInsets(padding), easingInterpolator);
}
@Override
- public void flyTo(@NonNull LatLng center, double zoom, double angle, double pitch, long duration) {
+ public void flyTo(@NonNull LatLng center, double zoom, double angle, double pitch, double[] padding, long duration) {
if (checkState("flyTo")) {
return;
}
- nativeFlyTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom);
+ nativeFlyTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom,
+ getAnimationPaddingAndClearCachedInsets(padding));
}
@Override
@@ -703,7 +701,11 @@ final class NativeMapView implements NativeMap {
if (checkState("getCameraValues")) {
return new CameraPosition.Builder().build();
}
- return nativeGetCameraPosition();
+ if (edgeInsets != null) {
+ return new CameraPosition.Builder(nativeGetCameraPosition()).padding(edgeInsets).build();
+ } else {
+ return nativeGetCameraPosition();
+ }
}
@Override
@@ -1126,7 +1128,7 @@ final class NativeMapView implements NativeMap {
private native void nativeMoveBy(double dx, double dy, long duration);
@Keep
- private native void nativeSetLatLng(double latitude, double longitude, long duration);
+ private native void nativeSetLatLng(double latitude, double longitude, double[] padding, long duration);
@NonNull
@Keep
@@ -1176,12 +1178,6 @@ final class NativeMapView implements NativeMap {
private native void nativeRotateBy(double sx, double sy, double ex, double ey, long duration);
@Keep
- private native void nativeSetContentPadding(float top, float left, float bottom, float right);
-
- @Keep
- private native float[] nativeGetContentPadding();
-
- @Keep
private native void nativeSetBearing(double degrees, long duration);
@Keep
@@ -1270,16 +1266,17 @@ final class NativeMapView implements NativeMap {
private native double nativeGetTopOffsetPixelsForAnnotationSymbol(String symbolName);
@Keep
- private native void nativeJumpTo(double angle, double latitude, double longitude, double pitch, double zoom);
+ private native void nativeJumpTo(double angle, double latitude, double longitude, double pitch, double zoom,
+ double[] padding);
@Keep
private native void nativeEaseTo(double angle, double latitude, double longitude,
- long duration, double pitch, double zoom,
+ long duration, double pitch, double zoom, double[] padding,
boolean easingInterpolator);
@Keep
private native void nativeFlyTo(double angle, double latitude, double longitude,
- long duration, double pitch, double zoom);
+ long duration, double pitch, double zoom, double[] padding);
@NonNull
@Keep
@@ -1437,6 +1434,20 @@ final class NativeMapView implements NativeMap {
return destroyed;
}
+ private double[] getAnimationPaddingAndClearCachedInsets(double[] providedPadding) {
+ if (providedPadding == null) {
+ providedPadding = this.edgeInsets;
+ }
+ this.edgeInsets = null;
+ return providedPadding == null ? null :
+ new double[] {
+ providedPadding[1] / pixelRatio,
+ providedPadding[0] / pixelRatio,
+ providedPadding[3] / pixelRatio,
+ providedPadding[2] / pixelRatio
+ };
+ }
+
public interface ViewCallback {
@Nullable
Bitmap getViewContent();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
index 8ed003b821..76395f9564 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
@@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.maps;
import android.graphics.PointF;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
+
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.constants.GeometryConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -24,7 +25,6 @@ public class Projection {
private final NativeMap nativeMapView;
@NonNull
private final MapView mapView;
- private int[] contentPadding = new int[] {0, 0, 0, 0};
Projection(@NonNull NativeMap nativeMapView, @NonNull MapView mapView) {
this.nativeMapView = nativeMapView;
@@ -32,8 +32,7 @@ public class Projection {
}
void setContentPadding(int[] contentPadding) {
- this.contentPadding = contentPadding;
- float[] output = new float[contentPadding.length];
+ double[] output = new double[contentPadding.length];
for (int i = 0; i < contentPadding.length; i++) {
output[i] = contentPadding[i];
}
@@ -41,11 +40,15 @@ public class Projection {
}
int[] getContentPadding() {
- return contentPadding;
+ double[] padding = nativeMapView.getCameraPosition().padding;
+ return new int[] {(int) padding[0], (int) padding[1], (int) padding[2], (int) padding[3]};
}
+ /**
+ * @deprecated unused
+ */
+ @Deprecated
public void invalidateContentPadding() {
- setContentPadding(contentPadding);
}
/**
@@ -126,10 +129,11 @@ public class Projection {
top = 0;
bottom = mapView.getHeight();
} else {
- left = (float) contentPadding[0];
- right = (float) (mapView.getWidth() - contentPadding[2]);
- top = (float) contentPadding[1];
- bottom = (float) (mapView.getHeight() - contentPadding[3]);
+ int[] contentPadding = getContentPadding();
+ left = contentPadding[0];
+ right = mapView.getWidth() - contentPadding[2];
+ top = contentPadding[1];
+ bottom = mapView.getHeight() - contentPadding[3];
}
LatLng center = fromScreenLocation(new PointF(left + (right - left) / 2, top + (bottom - top) / 2));
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java
index a707ab13da..b21186050b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java
@@ -523,14 +523,12 @@ public class Style {
for (Layer layer : layers.values()) {
if (layer != null) {
layer.setDetached();
- nativeMap.removeLayer(layer);
}
}
for (Source source : sources.values()) {
if (source != null) {
source.setDetached();
- nativeMap.removeSource(source);
}
}
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 1961cf0450..f785aacf3c 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
@@ -108,7 +108,8 @@ public final class Transform implements MapView.OnCameraDidChangeListener {
if (isValidCameraPosition(cameraPosition)) {
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
- nativeMap.jumpTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.tilt, cameraPosition.bearing);
+ nativeMap.jumpTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.tilt, cameraPosition.bearing,
+ cameraPosition.padding);
cameraChangeDispatcher.onCameraIdle();
invalidateCameraPosition();
handler.post(new Runnable() {
@@ -119,6 +120,8 @@ public final class Transform implements MapView.OnCameraDidChangeListener {
}
}
});
+ } else if (callback != null) {
+ callback.onFinish();
}
}
@@ -135,7 +138,9 @@ public final class Transform implements MapView.OnCameraDidChangeListener {
}
mapView.addOnCameraDidChangeListener(this);
nativeMap.easeTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.bearing, cameraPosition.tilt,
- durationMs, easingInterpolator);
+ cameraPosition.padding, durationMs, easingInterpolator);
+ } else if (callback != null) {
+ callback.onFinish();
}
}
@@ -155,7 +160,9 @@ public final class Transform implements MapView.OnCameraDidChangeListener {
}
mapView.addOnCameraDidChangeListener(this);
nativeMap.flyTo(cameraPosition.target, cameraPosition.zoom, cameraPosition.bearing,
- cameraPosition.tilt, durationMs);
+ cameraPosition.tilt, cameraPosition.padding, durationMs);
+ } else if (callback != null) {
+ callback.onFinish();
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
index 49abb70c91..3fd155837e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
@@ -67,6 +67,7 @@ public final class UiSettings {
private boolean flingVelocityAnimationEnabled = true;
private boolean increaseRotateThresholdWhenScaling = true;
+ private boolean disableRotateWhenScaling = true;
private boolean increaseScaleThresholdWhenRotating = true;
private float zoomRate = 1.0f;
@@ -132,6 +133,7 @@ public final class UiSettings {
outState.putBoolean(MapboxConstants.STATE_ROTATE_ANIMATION_ENABLED, isRotateVelocityAnimationEnabled());
outState.putBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED, isFlingVelocityAnimationEnabled());
outState.putBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD, isIncreaseRotateThresholdWhenScaling());
+ outState.putBoolean(MapboxConstants.STATE_DISABLE_ROTATE_WHEN_SCALING, isDisableRotateWhenScaling());
outState.putBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD, isIncreaseScaleThresholdWhenRotating());
outState.putBoolean(MapboxConstants.STATE_QUICK_ZOOM_ENABLED, isQuickZoomGesturesEnabled());
outState.putFloat(MapboxConstants.STATE_ZOOM_RATE, getZoomRate());
@@ -148,6 +150,7 @@ public final class UiSettings {
setFlingVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED));
setIncreaseRotateThresholdWhenScaling(
savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD));
+ setDisableRotateWhenScaling(savedInstanceState.getBoolean(MapboxConstants.STATE_DISABLE_ROTATE_WHEN_SCALING));
setIncreaseScaleThresholdWhenRotating(
savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD));
setQuickZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_QUICK_ZOOM_ENABLED));
@@ -414,10 +417,6 @@ public final class UiSettings {
}
void update(@NonNull CameraPosition cameraPosition) {
- if (!isCompassEnabled()) {
- return;
- }
-
double clockwiseBearing = -cameraPosition.bearing;
compassView.update(clockwiseBearing);
}
@@ -919,7 +918,9 @@ public final class UiSettings {
* Returns whether rotation threshold should be increase whenever scale is detected.
*
* @return If true, rotation threshold will be increased.
+ * @deprecated unused, see {@link #isDisableRotateWhenScaling()} instead
*/
+ @Deprecated
public boolean isIncreaseRotateThresholdWhenScaling() {
return increaseRotateThresholdWhenScaling;
}
@@ -928,12 +929,32 @@ public final class UiSettings {
* Set whether rotation threshold should be increase whenever scale is detected.
*
* @param increaseRotateThresholdWhenScaling If true, rotation threshold will be increased.
+ * @deprecated unused, see {@link #setDisableRotateWhenScaling(boolean)} instead
*/
+ @Deprecated
public void setIncreaseRotateThresholdWhenScaling(boolean increaseRotateThresholdWhenScaling) {
this.increaseRotateThresholdWhenScaling = increaseRotateThresholdWhenScaling;
}
/**
+ * Returns whether rotation gesture detector is disabled when scale is detected first.
+ *
+ * @return If true, rotation gesture detector will be disabled when scale is detected first.
+ */
+ public boolean isDisableRotateWhenScaling() {
+ return disableRotateWhenScaling;
+ }
+
+ /**
+ * Set whether rotation gesture detector should be disabled when scale is detected first.
+ *
+ * @param disableRotateWhenScaling If true, rotation gesture detector will be disabled when scale is detected first.
+ */
+ public void setDisableRotateWhenScaling(boolean disableRotateWhenScaling) {
+ this.disableRotateWhenScaling = disableRotateWhenScaling;
+ }
+
+ /**
* Returns whether scale threshold should be increase whenever rotation is detected.
*
* @return If true, scale threshold will be increased.
@@ -1048,6 +1069,7 @@ public final class UiSettings {
*/
public void invalidate() {
setLogoMargins(getLogoMarginLeft(), getLogoMarginTop(), getLogoMarginRight(), getLogoMarginBottom());
+ setCompassEnabled(isCompassEnabled());
setCompassMargins(getCompassMarginLeft(), getCompassMarginTop(), getCompassMarginRight(), getCompassMarginBottom());
setAttributionMargins(getAttributionMarginLeft(), getAttributionMarginTop(), getAttributionMarginRight(),
getAttributionMarginBottom());
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
index 833a875a09..947ed35555 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
@@ -8,7 +8,6 @@ import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.log.Logger;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.storage.FileSource;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -36,10 +35,9 @@ public abstract class MapRenderer implements MapRendererScheduler {
public MapRenderer(@NonNull Context context, String localIdeographFontFamily) {
float pixelRatio = context.getResources().getDisplayMetrics().density;
- String programCacheDir = FileSource.getInternalCachePath(context);
// Initialise native peer
- nativeInitialize(this, pixelRatio, programCacheDir, localIdeographFontFamily);
+ nativeInitialize(this, pixelRatio, localIdeographFontFamily);
}
public void onStart() {
@@ -119,7 +117,6 @@ public abstract class MapRenderer implements MapRendererScheduler {
private native void nativeInitialize(MapRenderer self,
float pixelRatio,
- String programCacheDir,
String localIdeographFontFamily);
@CallSuper
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
index 165b15a512..205e35641b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
@@ -5,9 +5,13 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.view.TextureView;
+
import com.mapbox.mapboxsdk.log.Logger;
import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
@@ -15,8 +19,6 @@ import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
/**
* The render thread is responsible for managing the communication between the
@@ -250,13 +252,13 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
// Initialize EGL
if (initializeEGL) {
eglHolder.prepare();
- if (!eglHolder.createSurface()) {
- synchronized (lock) {
+ synchronized (lock) {
+ if (!eglHolder.createSurface()) {
// Cleanup the surface if one could not be created
// and wait for another to be ready.
destroySurface = true;
+ continue;
}
- continue;
}
mapRenderer.onSurfaceCreated(gl, eglHolder.eglConfig);
mapRenderer.onSurfaceChanged(gl, w, h);
@@ -265,7 +267,9 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
// If the surface size has changed inform the map renderer.
if (recreateSurface) {
- eglHolder.createSurface();
+ synchronized (lock) {
+ eglHolder.createSurface();
+ }
mapRenderer.onSurfaceChanged(gl, w, h);
continue;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
index 53c0c1c60f..b593076dc1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
@@ -91,6 +91,7 @@ public final class CompassView extends ImageView implements Runnable {
resetAnimation();
setAlpha(1.0f);
setVisibility(View.VISIBLE);
+ update(rotation);
} else {
resetAnimation();
setAlpha(0.0f);
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 5bd0dd4bf6..faed46662a 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
@@ -307,7 +307,7 @@ public class OfflineManager {
}
/**
- * Forces revalidation of the ambient cache.
+ * Forces re-validation of the ambient cache.
* <p>
* Forces Mapbox GL Native to revalidate resources stored in the ambient
* cache with the tile server before using them, making sure they
@@ -537,7 +537,13 @@ public class OfflineManager {
}
/**
- * Create an offline region in the database.
+ * Creates an offline region in the database by downloading the resources needed to use
+ * the given region offline.
+ * <p>
+ * As of version 8.3.0 of the Maps SDK for Android, offline tile requests are no longer exempt from
+ * billing on mobile. Developers are subject to separate Vector Tile or Raster Tile API pricing for
+ * offline use. See <a href="https://www.mapbox.com/pricing/">our pricing page</a> for more information.
+ * </p>
* <p>
* When the initial database queries have completed, the provided callback will be
* executed on the main thread.
@@ -609,10 +615,15 @@ public class OfflineManager {
}
/**
- * Changing or bypassing this limit without permission from Mapbox is prohibited
- * by the Mapbox Terms of Service.
+ * Sets the maximum number of Mapbox-hosted tiles that may be downloaded and stored on the current device.
+ * By default, the limit is set to 6,000.
+ * <p>
+ * Once this limit is reached, {@link OfflineRegion.OfflineRegionObserver#mapboxTileCountLimitExceeded(long)}
+ * fires every additional attempt to download additional tiles until already downloaded tiles are removed
+ * by calling {@link OfflineRegion#deleteOfflineRegion(OfflineRegion.OfflineRegionDeleteCallback)}.
+ * </p>
*
- * @param limit the new tile count limit.
+ * @param limit the maximum number of tiles allowed to be downloaded
*/
@Keep
public native void setOfflineMapboxTileCountLimit(long limit);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
index e8e38c4f4e..1e0069c25f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
@@ -18,17 +18,20 @@ import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.attribution.AttributionLayout;
import com.mapbox.mapboxsdk.attribution.AttributionMeasure;
import com.mapbox.mapboxsdk.attribution.AttributionParser;
import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.log.Logger;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
import com.mapbox.mapboxsdk.storage.FileSource;
+import com.mapbox.mapboxsdk.utils.FontUtils;
import com.mapbox.mapboxsdk.utils.ThreadUtils;
/**
@@ -98,7 +101,7 @@ public class MapSnapshotter {
private LatLngBounds region;
private CameraPosition cameraPosition;
private boolean showLogo = true;
- private String localIdeographFontFamily = "sans-serif";
+ private String localIdeographFontFamily = MapboxConstants.DEFAULT_FONT;
private String apiBaseUrl;
/**
@@ -182,14 +185,31 @@ public class MapSnapshotter {
* <p>
* The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}.
* Default system fonts are defined in &#x27;/system/etc/fonts.xml&#x27;
- * Default font for local ideograph font family is "sans-serif".
- *
+ * Default font for local ideograph font family is {@link MapboxConstants#DEFAULT_FONT}.
+ * </p>
* @param fontFamily font family for local ideograph generation.
* @return the mutated {@link Options}
*/
@NonNull
public Options withLocalIdeographFontFamily(String fontFamily) {
- this.localIdeographFontFamily = fontFamily;
+ this.localIdeographFontFamily = FontUtils.extractValidFont(fontFamily);
+ return this;
+ }
+
+ /**
+ * Set a font family from range of font families for generating glyphs locally for ideographs in the
+ * &#x27;CJK Unified Ideographs&#x27; and &#x27;Hangul Syllables&#x27; ranges.
+ * <p>
+ * The font families are checked against the default system fonts defined in
+ * &#x27;/system/etc/fonts.xml&#x27;. Default font for local ideograph font family is
+ * {@link MapboxConstants#DEFAULT_FONT}.
+ * </p>
+ * @param fontFamilies font families for local ideograph generation.
+ * @return the mutated {@link Options}
+ */
+ @NonNull
+ public Options withLocalIdeographFontFamily(String... fontFamilies) {
+ this.localIdeographFontFamily = FontUtils.extractValidFont(fontFamilies);
return this;
}
@@ -274,7 +294,7 @@ public class MapSnapshotter {
/**
* @return the font family used for locally generating ideographs,
- * Default font for local ideograph font family is "sans-serif".
+ * Default font for local ideograph font family is {@link MapboxConstants#DEFAULT_FONT}.
*/
public String getLocalIdeographFontFamily() {
return localIdeographFontFamily;
@@ -319,11 +339,9 @@ public class MapSnapshotter {
fileSource.setApiBaseUrl(apiBaseUrl);
}
- String programCacheDir = FileSource.getInternalCachePath(context);
-
nativeInitialize(this, fileSource, options.pixelRatio, options.width,
options.height, options.styleUri, options.styleJson, options.region, options.cameraPosition,
- options.showLogo, programCacheDir, options.localIdeographFontFamily);
+ options.showLogo, options.localIdeographFontFamily);
}
/**
@@ -605,8 +623,7 @@ public class MapSnapshotter {
FileSource fileSource, float pixelRatio,
int width, int height, String styleUrl, String styleJson,
LatLngBounds region, CameraPosition position,
- boolean showLogo, String programCacheDir,
- String localIdeographFontFamily);
+ boolean showLogo, String localIdeographFontFamily);
@Keep
protected native void nativeStart();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
index 0f5ce76725..34b3308809 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
@@ -5,7 +5,6 @@ import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.Size;
-
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@@ -1436,6 +1435,27 @@ public class Expression {
}
/**
+ * Gets the value of a cluster property accumulated so far. Can only be used in the clusterProperties
+ * option of a clustered GeoJSON source.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * GeoJsonOptions options = new GeoJsonOptions()
+ * .withCluster(true)
+ * .withClusterProperty("max", max(accumulated(), get("max")).toArray(), get("mag").toArray());
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-accumulated">Style specification</a>
+ */
+ public static Expression accumulated() {
+ return new Expression("accumulated");
+ }
+
+ /**
* Gets the kernel density estimation of a pixel in a heatmap layer,
* which is a relative measure of how many data points are crowded around a particular pixel.
* Can only be used in the `heatmap-color` property.
@@ -3124,6 +3144,40 @@ public class Expression {
}
/**
+ * Converts the input number into a string representation using the providing formatting rules.
+ * If set, the locale argument specifies the locale to use, as a BCP 47 language tag.
+ * If set, the currency argument specifies an ISO 4217 code to use for currency-style formatting.
+ * If set, the min-fraction-digits and max-fraction-digits arguments specify the minimum and maximum number
+ * of fractional digits to include.
+ *
+ * @param number number expression
+ * @param options number formatting options
+ * @return expression
+ */
+ public static Expression numberFormat(@NonNull Expression number, @NonNull NumberFormatOption... options) {
+ final Map<String, Expression> map = new HashMap<>();
+ for (NumberFormatOption option : options) {
+ map.put(option.type, option.value);
+ }
+ return new Expression("number-format", number, new ExpressionMap(map));
+ }
+
+ /**
+ * Converts the input number into a string representation using the providing formatting rules.
+ * If set, the locale argument specifies the locale to use, as a BCP 47 language tag.
+ * If set, the currency argument specifies an ISO 4217 code to use for currency-style formatting.
+ * If set, the min-fraction-digits and max-fraction-digits arguments specify the minimum and maximum number
+ * of fractional digits to include.
+ *
+ * @param number number expression
+ * @param options number formatting options
+ * @return expression
+ */
+ public static Expression numberFormat(@NonNull Number number, @NonNull NumberFormatOption... options) {
+ return numberFormat(literal(number), options);
+ }
+
+ /**
* Asserts that the input value is a boolean.
* If multiple values are provided, each one is evaluated in order until a boolean value is obtained.
* If none of the inputs are booleans, the expression is an error.
@@ -4365,20 +4419,137 @@ public class Expression {
}
/**
+ * Base class for an option entry that is encapsulated as a json object member for an expression.
+ */
+ private static class Option {
+ @NonNull
+ String type;
+ @NonNull
+ Expression value;
+
+ /**
+ * Create an option option entry that is encapsulated as a json object member for an expression.
+ *
+ * @param type json object member name
+ * @param value json object member value
+ */
+ Option(@NonNull String type, @NonNull Expression value) {
+ this.type = type;
+ this.value = value;
+ }
+ }
+
+ /**
+ * Holds format options used in a {@link #numberFormat(Number, NumberFormatOption...)} expression.
+ */
+ public static class NumberFormatOption extends Option {
+
+ /**
+ * {@inheritDoc}
+ */
+ NumberFormatOption(@NonNull String type, @NonNull Expression value) {
+ super(type, value);
+ }
+
+ /**
+ * Number formatting option for specifying the locale to use, as a BCP 47 language tag.
+ *
+ * @param string the locale to use while performing number formatting
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption locale(@NonNull Expression string) {
+ return new NumberFormatOption("locale", string);
+ }
+
+ /**
+ * Number formatting option for specifying the locale to use, as a BCP 47 language tag.
+ *
+ * @param string the locale to use while performing number formatting
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption locale(@NonNull String string) {
+ return new NumberFormatOption("locale", literal(string));
+ }
+
+ /**
+ * Number formatting option for specifying the currency to use, an ISO 4217 code.
+ *
+ * @param string the currency to use while performing number formatting
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption currency(@NonNull Expression string) {
+ return new NumberFormatOption("currency", string);
+ }
+
+ /**
+ * Number formatting options for specifying the currency to use, an ISO 4217 code.
+ *
+ * @param string the currency to use while performing number formatting
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption currency(@NonNull String string) {
+ return new NumberFormatOption("currency", literal(string));
+ }
+
+ /**
+ * Number formatting options for specifying the minimum fraction digits to include.
+ *
+ * @param number the amount of minimum fraction digits to include
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption minFractionDigits(@NonNull Expression number) {
+ return new NumberFormatOption("min-fraction-digits", number);
+ }
+
+ /**
+ * Number formatting options for specifying the minimum fraction digits to include.
+ *
+ * @param number the amount of minimum fraction digits to include
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption minFractionDigits(int number) {
+ return new NumberFormatOption("min-fraction-digits", literal(number));
+ }
+
+ /**
+ * Number formatting options for specifying the maximum fraction digits to include.
+ *
+ * @param number the amount of minimum fraction digits to include
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption maxFractionDigits(@NonNull Expression number) {
+ return new NumberFormatOption("max-fraction-digits", number);
+ }
+
+ /**
+ * Number formatting options for specifying the maximum fraction digits to include.
+ *
+ * @param number the amount of minimum fraction digits to include
+ * @return number format option
+ */
+ @NonNull
+ public static NumberFormatOption maxFractionDigits(@NonNull int number) {
+ return new NumberFormatOption("max-fraction-digits", literal(number));
+ }
+ }
+
+ /**
* Holds format options used in a {@link #formatEntry(Expression, FormatOption...)} that builds
* a {@link #format(FormatEntry...)} expression.
* <p>
* If an option is not set, it defaults to the base value defined for the symbol.
*/
- public static class FormatOption {
- @NonNull
- private String type;
- @NonNull
- private Expression value;
+ public static class FormatOption extends Option {
FormatOption(@NonNull String type, @NonNull Expression value) {
- this.type = type;
- this.value = value;
+ super(type, value);
}
/**
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 8ffcf6ddb8..52ed6c23ae 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
@@ -669,6 +669,27 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface ANCHOR {}
+ // TEXT_WRITING_MODE: The property allows control over a symbol's orientation. Note that the property values act as a hint, so that a symbol whose language doesn’t support the provided orientation will be laid out in its natural orientation. Example: English point symbol will be rendered horizontally even if array value contains single 'vertical' enum value. The order of elements in an array define priority order for the placement of an orientation variant.
+
+ /**
+ * If a text's language supports horizontal writing mode, symbols with point placement would be laid out horizontally.
+ */
+ public static final String TEXT_WRITING_MODE_HORIZONTAL = "horizontal";
+ /**
+ * If a text's language supports vertical writing mode, symbols with point placement would be laid out vertically.
+ */
+ public static final String TEXT_WRITING_MODE_VERTICAL = "vertical";
+
+ /**
+ * The property allows control over a symbol's orientation. Note that the property values act as a hint, so that a symbol whose language doesn’t support the provided orientation will be laid out in its natural orientation. Example: English point symbol will be rendered horizontally even if array value contains single 'vertical' enum value. The order of elements in an array define priority order for the placement of an orientation variant.
+ */
+ @StringDef({
+ TEXT_WRITING_MODE_HORIZONTAL,
+ TEXT_WRITING_MODE_VERTICAL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_WRITING_MODE {}
+
private Property() {
}
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 3f2771a307..66a6fe20b2 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
@@ -2296,7 +2296,7 @@ public class PropertyFactory {
}
/**
- * Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which doesn't support the two-dimensional {@link PropertyFactory#textOffset}.
+ * Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which defaults to using the two-dimensional {@link PropertyFactory#textOffset} if present.
*
* @param value a Float value
* @return property wrapper around Float
@@ -2306,7 +2306,7 @@ public class PropertyFactory {
}
/**
- * Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which doesn't support the two-dimensional {@link PropertyFactory#textOffset}.
+ * Radial offset of text, in the direction of the symbol's anchor. Useful in combination with {@link PropertyFactory#textVariableAnchor}, which defaults to using the two-dimensional {@link PropertyFactory#textOffset} if present.
*
* @param value a Float value
* @return property wrapper around Float
@@ -2316,7 +2316,7 @@ public class PropertyFactory {
}
/**
- * To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} instead of the two-dimensional {@link PropertyFactory#textOffset}.
+ * To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} or the two-dimensional {@link PropertyFactory#textOffset}.
*
* @param value a String[] value
* @return property wrapper around String[]
@@ -2326,7 +2326,7 @@ public class PropertyFactory {
}
/**
- * To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} instead of the two-dimensional {@link PropertyFactory#textOffset}.
+ * To increase the chance of placing high-priority labels on the map, you can provide an array of {@link Property.TEXT_ANCHOR} locations: the render will attempt to place the label at each location, in order, before moving onto the next label. Use `text-justify: auto` to choose justification based on anchor position. To apply an offset, use the {@link PropertyFactory#textRadialOffset} or the two-dimensional {@link PropertyFactory#textOffset}.
*
* @param value a String[] value
* @return property wrapper around String[]
@@ -2376,6 +2376,26 @@ public class PropertyFactory {
}
/**
+ * The property allows control over a symbol's orientation. Note that the property values act as a hint, so that a symbol whose language doesn’t support the provided orientation will be laid out in its natural orientation. Example: English point symbol will be rendered horizontally even if array value contains single 'vertical' enum value. The order of elements in an array define priority order for the placement of an orientation variant.
+ *
+ * @param value a String[] value
+ * @return property wrapper around String[]
+ */
+ public static PropertyValue<String[]> textWritingMode(String[] value) {
+ return new LayoutPropertyValue<>("text-writing-mode", value);
+ }
+
+ /**
+ * The property allows control over a symbol's orientation. Note that the property values act as a hint, so that a symbol whose language doesn’t support the provided orientation will be laid out in its natural orientation. Example: English point symbol will be rendered horizontally even if array value contains single 'vertical' enum value. The order of elements in an array define priority order for the placement of an orientation variant.
+ *
+ * @param value a String[] value
+ * @return property wrapper around String[]
+ */
+ public static PropertyValue<Expression> textWritingMode(Expression value) {
+ return new LayoutPropertyValue<>("text-writing-mode", value);
+ }
+
+ /**
* Rotates the text clockwise.
*
* @param value a Float value
@@ -2456,7 +2476,7 @@ public class PropertyFactory {
}
/**
- * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up. If used with text-variable-anchor, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position.
*
* @param value a Float[] value
* @return property wrapper around Float[]
@@ -2466,7 +2486,7 @@ public class PropertyFactory {
}
/**
- * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up. If used with text-variable-anchor, input values will be taken as absolute values. Offsets along the x- and y-axis will be applied automatically based on the anchor position.
*
* @param value a Float[] value
* @return property wrapper around Float[]
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 1d8bad7e8e..5ab47def4e 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
@@ -530,6 +530,18 @@ public class SymbolLayer extends Layer {
}
/**
+ * Get the TextWritingMode property
+ *
+ * @return property wrapper value around String[]
+ */
+ @NonNull
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String[]> getTextWritingMode() {
+ checkThread();
+ return (PropertyValue<String[]>) new PropertyValue("text-writing-mode", nativeGetTextWritingMode());
+ }
+
+ /**
* Get the TextRotate property
*
* @return property wrapper value around Float
@@ -1243,6 +1255,10 @@ public class SymbolLayer extends Layer {
@NonNull
@Keep
+ private native Object nativeGetTextWritingMode();
+
+ @NonNull
+ @Keep
private native Object nativeGetTextRotate();
@NonNull
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
index 1e1b9bafa6..091079ab92 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
@@ -1,9 +1,12 @@
package com.mapbox.mapboxsdk.style.sources;
import android.support.annotation.NonNull;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import java.util.HashMap;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.ExpressionLiteral;
+
/**
* Builder class for composing GeoJsonSource objects.
*
@@ -109,4 +112,28 @@ public class GeoJsonOptions extends HashMap<String, Object> {
this.put("clusterRadius", clusterRadius);
return this;
}
+
+ /**
+ * An object defining custom properties on the generated clusters if clustering is enabled,
+ * aggregating values from clustered points. Has the form {"property_name": [operator, [map_expression]]} or
+ * {"property_name": [[operator, accumulated, expression], [map_expression]]}
+ *
+ * @param propertyName name of the property
+ * @param operatorExpr operatorExpr is any expression function that accepts at least 2 operands (e.g. "+" or "max").
+ * It accumulates the property value from clusters/points the cluster contains. It can either be
+ * a literal with single operator, or be a valid expression
+ * @param mapExpr map expression produces the value of a single point, it shall be a valid expression
+ * @return the current instance for chaining
+ */
+ @NonNull
+ public GeoJsonOptions withClusterProperty(String propertyName, Expression operatorExpr, Expression mapExpr) {
+ HashMap<String, Object[]> properties = containsKey("clusterProperties")
+ ? (HashMap<String, Object[]>) get("clusterProperties") : new HashMap<String, Object[]>();
+ Object operator = (operatorExpr instanceof ExpressionLiteral)
+ ? ((ExpressionLiteral)operatorExpr).toValue() : operatorExpr.toArray();
+ Object map = mapExpr.toArray();
+ properties.put(propertyName, new Object[]{operator, map});
+ this.put("clusterProperties", properties);
+ return this;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/FontUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/FontUtils.java
new file mode 100644
index 0000000000..627b342179
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/FontUtils.java
@@ -0,0 +1,81 @@
+package com.mapbox.mapboxsdk.utils;
+
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+import com.mapbox.mapboxsdk.MapStrictMode;
+import com.mapbox.mapboxsdk.log.Logger;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static com.mapbox.mapboxsdk.constants.MapboxConstants.DEFAULT_FONT;
+
+/**
+ * Utility class to select a font from a range of font names based on the availability of fonts on the device.
+ */
+public class FontUtils {
+
+ private static final String TAG = "Mbgl-FontUtils";
+ private static final String TYPEFACE_FONTMAP_FIELD_NAME = "sSystemFontMap";
+ private static final List<String> DEFAULT_FONT_STACKS = new ArrayList<>();
+
+ static {
+ DEFAULT_FONT_STACKS.add("sans-serif");
+ DEFAULT_FONT_STACKS.add("serif");
+ DEFAULT_FONT_STACKS.add("monospace");
+ }
+
+ private FontUtils() {
+ // no instance
+ }
+
+ /**
+ * Select a font from a range of font names to match the availability of fonts on the device.
+ *
+ * @param fontNames the range of font names to select from
+ * @return the selected fon
+ */
+ public static String extractValidFont(String... fontNames) {
+ if (fontNames == null) {
+ return null;
+ }
+
+ List<String> validFonts;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ validFonts = getDeviceFonts();
+ } else {
+ validFonts = DEFAULT_FONT_STACKS;
+ }
+
+ for (String fontName : fontNames) {
+ if (validFonts.contains(fontName)) {
+ return fontName;
+ }
+ }
+
+ Logger.i(TAG, String.format(
+ "Couldn't map font family for local ideograph, using %s instead", DEFAULT_FONT)
+ );
+ return DEFAULT_FONT;
+ }
+
+ @SuppressWarnings( {"JavaReflectionMemberAccess", "unchecked"})
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ private static List<String> getDeviceFonts() {
+ List<String> fonts = new ArrayList<>();
+ try {
+ Typeface typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
+ Field field = Typeface.class.getDeclaredField(TYPEFACE_FONTMAP_FIELD_NAME);
+ field.setAccessible(true);
+ Map<String, Typeface> fontMap = (Map<String, Typeface>) field.get(typeface);
+ fonts.addAll(fontMap.keySet());
+ } catch (Exception exception) {
+ Logger.e(TAG, "Couldn't load fonts from Typeface", exception);
+ MapStrictMode.strictModeViolation("Couldn't load fonts from Typeface", exception);
+ }
+ return fonts;
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
index 0c90e4b244..7ec3262c57 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
@@ -46,4 +46,19 @@ public class MathUtils {
return secondMod + min;
}
+
+ /**
+ * Scale a value from an arbitrary range to a normalized range.
+ *
+ * @param x The value to be normalized.
+ * @param dataLow lowest expected value from a data set
+ * @param dataHigh highest expected value from a data set
+ * @param normalizedLow normalized lowest value
+ * @param normalizedHigh normalized highest value
+ * @return The result of the normalization.
+ */
+ public static double normalize(double x, double dataLow, double dataHigh,
+ double normalizedLow, double normalizedHigh) {
+ return ((x - dataLow) / (dataHigh - dataLow)) * (normalizedHigh - normalizedLow) + normalizedLow;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
index 3e7124a414..ff8a32ac64 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -7,7 +7,9 @@
<!--Configuration-->
<attr name="mapbox_apiBaseUri" format="string"/>
+ <attr name="mapbox_localIdeographEnabled" format="boolean"/>
<attr name="mapbox_localIdeographFontFamily" format="string"/>
+ <attr name="mapbox_localIdeographFontFamilies" format="reference"/>
<attr name="mapbox_cross_source_collisions" format="boolean"/>
<!--Camera-->
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index d2428d8d8f..012e46cc07 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -3,18 +3,26 @@
<dimen name="mapbox_infowindow_tipview_width">8dp</dimen>
<dimen name="mapbox_infowindow_margin">8dp</dimen>
<dimen name="mapbox_four_dp">4dp</dimen>
+ <dimen name="mapbox_minimum_scale_speed">0.6dp</dimen>
+ <dimen name="mapbox_minimum_angled_scale_speed">0.9dp</dimen>
<dimen name="mapbox_eight_dp">8dp</dimen>
<dimen name="mapbox_ninety_two_dp">92dp</dimen>
<dimen name="mapbox_my_locationview_outer_circle">18dp</dimen>
<!--Minimum scale velocity required to start animation-->
- <dimen name="mapbox_minimum_scale_velocity">150dp</dimen>
+ <dimen name="mapbox_minimum_scale_velocity">225dp</dimen>
<!--Minimum scale span delta required to execute scale gesture when rotating-->
- <dimen name="mapbox_minimum_scale_span_when_rotating">100dp</dimen>
+ <dimen name="mapbox_minimum_scale_span_when_rotating">75dp</dimen>
<!--Minimum angular velocity required to start rotation animation-->
- <dimen name="mapbox_minimum_angular_velocity">0.025dp</dimen>
+ <dimen name="mapbox_minimum_angular_velocity">0.10dp</dimen>
+
+ <!--Angular velocity multiplier. Adapts the velocity for screen density-->
+ <dimen name="mapbox_angular_velocity_multiplier">1.3dp</dimen>
+
+ <!--Screen density constant to adjust gesture's velocityXY thresholds-->
+ <dimen name="mapbox_density_constant">0.29dp</dimen>
<dimen name="mapbox_locationComponentTrackingInitialMoveThreshold">25dp</dimen>
<dimen name="mapbox_locationComponentTrackingMultiFingerMoveThreshold">400dp</dimen>
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
index 0c5f3a4be2..6974705fae 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
@@ -1,21 +1,24 @@
package com.mapbox.mapboxsdk.camera;
import android.content.res.TypedArray;
-import android.os.Parcelable;
+import android.os.Parcel;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.utils.MockParcel;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+@RunWith(RobolectricTestRunner.class)
public class CameraPositionTest {
private static final double DELTA = 1e-15;
@@ -23,7 +26,7 @@ public class CameraPositionTest {
@Test
public void testSanity() {
LatLng latLng = new LatLng(1, 2);
- CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5);
+ CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0});
assertNotNull("cameraPosition should not be null", cameraPosition);
}
@@ -32,9 +35,10 @@ public class CameraPositionTest {
TypedArray typedArray = null;
CameraPosition cameraPosition = new CameraPosition.Builder(typedArray).build();
assertEquals("bearing should match", -1, cameraPosition.bearing, DELTA);
- assertEquals("latlng should match", null, cameraPosition.target);
+ assertNull("latlng should be null", cameraPosition.target);
assertEquals("tilt should match", -1, cameraPosition.tilt, DELTA);
assertEquals("zoom should match", -1, cameraPosition.zoom, DELTA);
+ assertNull("padding should be null", cameraPosition.padding);
}
@Test
@@ -63,16 +67,16 @@ public class CameraPositionTest {
@Test
public void testToString() {
LatLng latLng = new LatLng(1, 2);
- CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5);
+ CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0});
assertEquals("toString should match", "Target: LatLng [latitude=1.0, longitude=2.0, altitude=0.0], Zoom:3.0, "
- + "Bearing:5.0, Tilt:4.0", cameraPosition.toString());
+ + "Bearing:5.0, Tilt:4.0, Padding:[0.0, 500.0, 0.0, 0.0]", cameraPosition.toString());
}
@Test
public void testHashcode() {
LatLng latLng = new LatLng(1, 2);
- CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5);
- assertEquals("hashCode should match", -1007681505, cameraPosition.hashCode());
+ CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0});
+ assertEquals("hashCode should match", -420915327, cameraPosition.hashCode());
}
@Test
@@ -86,11 +90,12 @@ public class CameraPositionTest {
@Test
public void testEquals() {
LatLng latLng = new LatLng(1, 2);
- CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5);
- CameraPosition cameraPositionBearing = new CameraPosition(latLng, 3, 4, 9);
- CameraPosition cameraPositionTilt = new CameraPosition(latLng, 3, 9, 5);
- CameraPosition cameraPositionZoom = new CameraPosition(latLng, 9, 4, 5);
- CameraPosition cameraPositionTarget = new CameraPosition(new LatLng(), 3, 4, 5);
+ CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0});
+ CameraPosition cameraPositionBearing = new CameraPosition(latLng, 3, 4, 9, new double[] {0, 500, 0, 0});
+ CameraPosition cameraPositionTilt = new CameraPosition(latLng, 3, 9, 5, new double[] {0, 500, 0, 0});
+ CameraPosition cameraPositionZoom = new CameraPosition(latLng, 9, 4, 5, new double[] {0, 500, 0, 0});
+ CameraPosition cameraPositionTarget = new CameraPosition(new LatLng(), 3, 4, 5, new double[] {0, 500, 0, 0});
+ CameraPosition cameraPositionPadding = new CameraPosition(new LatLng(), 3, 4, 5, new double[] {0, 501, 0, 0});
assertEquals("cameraPosition should match itself", cameraPosition, cameraPosition);
assertNotEquals("cameraPosition should not match null", null, cameraPosition);
@@ -99,12 +104,17 @@ public class CameraPositionTest {
assertNotEquals("cameraPosition should not match for tilt", cameraPositionTilt, cameraPosition);
assertNotEquals("cameraPosition should not match for zoom", cameraPositionZoom, cameraPosition);
assertNotEquals("cameraPosition should not match for target", cameraPositionTarget, cameraPosition);
+ assertNotEquals("cameraPosition should not match for padding", cameraPositionPadding, cameraPosition);
}
@Test
public void testParcelable() {
- CameraPosition object = new CameraPosition(new LatLng(1, 2), 3, 4, 5);
- Parcelable parcelable = MockParcel.obtain(object);
- assertEquals("Parcel should match original object", parcelable, object);
+ CameraPosition cameraPosition1 = new CameraPosition(new LatLng(1, 2), 3, 4, 5, new double[] {0, 500, 0, 0});
+ Parcel parcel = Parcel.obtain();
+ cameraPosition1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ CameraPosition cameraPosition2 = CameraPosition.CREATOR.createFromParcel(parcel);
+ assertEquals("Parcel should match original object", cameraPosition1, cameraPosition2);
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java
index 0fbc47df55..ed2d015d85 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java
@@ -632,6 +632,41 @@ public class LocationLayerControllerTest {
verify(internalRenderModeChangedListener, times(1)).onRenderModeChanged(RenderMode.GPS);
}
+ @Test
+ public void layerHidden_renderModeChanged_layerShown_foregroundIconUpdated() {
+ OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ int drawableResId = 123;
+ int tintColor = 456;
+ when(options.foregroundDrawable()).thenReturn(drawableResId);
+ when(options.foregroundTintColor()).thenReturn(tintColor);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
+
+ LocationLayerController controller =
+ new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options),
+ bitmapProvider, options, internalRenderModeChangedListener);
+
+ verify(style).addImage(FOREGROUND_ICON, bitmap);
+
+ int drawableGpsResId = 789;
+ when(options.gpsDrawable()).thenReturn(drawableGpsResId);
+
+ Bitmap bitmapGps = mock(Bitmap.class);
+ when(bitmapProvider.generateBitmap(drawableGpsResId, tintColor)).thenReturn(bitmapGps);
+
+ controller.hide();
+
+ controller.setRenderMode(RenderMode.GPS);
+
+ controller.show();
+
+ verify(style).addImage(FOREGROUND_ICON, bitmapGps);
+ }
+
private LayerFeatureProvider buildFeatureProvider(@NonNull LocationComponentOptions options) {
LayerFeatureProvider provider = mock(LayerFeatureProvider.class);
when(provider.generateLocationFeature(null, options)).thenReturn(mock(Feature.class));
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt
new file mode 100644
index 0000000000..ee8024257f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt
@@ -0,0 +1,101 @@
+package com.mapbox.mapboxsdk.maps
+
+import android.content.Context
+import android.content.res.Resources
+import android.content.res.TypedArray
+import com.mapbox.mapboxsdk.R
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.RelaxedMockK
+import io.mockk.verify
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class MapboxMapOptionsAttrsTest {
+
+ @RelaxedMockK
+ private lateinit var options: MapboxMapOptions
+
+ @RelaxedMockK
+ private lateinit var typedArray: TypedArray
+
+ @RelaxedMockK
+ private lateinit var context: Context
+
+ @RelaxedMockK
+ private lateinit var resources: Resources
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+ every {
+ context.resources
+ }.returns(resources)
+ }
+
+ @Test
+ fun enabledLocalIdeographFontFamily() {
+ mockEnableLocalIdeograph(enabled = true)
+
+ val options = MapboxMapOptions.createFromAttributes(options, context, typedArray)
+
+ verify(exactly = 1) {
+ options.localIdeographFontFamily(any())
+ }
+ }
+
+ @Test
+ fun localIdeographFontFamily() {
+ mockEnableLocalIdeograph(enabled = true)
+
+ val font = "foo"
+ mockLocalIdeographString(font)
+
+ val options = MapboxMapOptions.createFromAttributes(options, context, typedArray)
+
+ verify(exactly = 1) {
+ options.localIdeographFontFamily(font)
+ }
+ }
+
+ @Test
+ fun localIdeographFontFamilies() {
+ mockEnableLocalIdeograph(enabled = true)
+
+ val fonts = arrayOf("foo", "bar")
+ mockLocalIdeographStringArray(fonts)
+
+ val options = MapboxMapOptions.createFromAttributes(options, context, typedArray)
+
+ verify(exactly = 1) {
+ options.localIdeographFontFamily(*fonts)
+ }
+ }
+
+ private fun mockEnableLocalIdeograph(enabled: Boolean) {
+ every {
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_localIdeographEnabled, true)
+ }.returns(enabled)
+ }
+
+ private fun mockLocalIdeographString(font: String) {
+ every {
+ typedArray.getString(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamily)
+ }.returns(font)
+ }
+
+ private fun mockLocalIdeographStringArray(fonts: Array<String>) {
+ val resId = 9000
+
+ every {
+ typedArray.getResourceId(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamilies, 0)
+ }.returns(resId)
+
+ every {
+ resources.getStringArray(resId)
+ }.returns(fonts)
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
index 11035c050f..6978afcf1f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
@@ -180,7 +180,7 @@ public class MapboxMapOptionsTest {
@Test
public void testLocalIdeographFontFamily_enabledByDefault() {
MapboxMapOptions options = MapboxMapOptions.createFromAttributes(RuntimeEnvironment.application, null);
- assertEquals("sans-serif", options.getLocalIdeographFontFamily());
+ assertEquals(MapboxConstants.DEFAULT_FONT, options.getLocalIdeographFontFamily());
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt
index d618f17500..a5070ae5c0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt
@@ -405,22 +405,4 @@ class StyleTest {
Assert.assertEquals("Layer that failed to be added shouldn't be cached", layer1, mapboxMap.style!!.getLayer("layer1"))
}
}
-
- @Test
- fun testClearRemovesSourcesFirst() {
- val source1 = mockk<GeoJsonSource>(relaxed = true)
- every { source1.id } returns "source1"
- val layer1 = mockk<SymbolLayer>(relaxed = true)
- every { layer1.id } returns "layer1"
-
- val builder = Style.Builder().withLayer(layer1).withSource(source1)
- mapboxMap.setStyle(builder)
- mapboxMap.notifyStyleLoaded()
- mapboxMap.setStyle(Style.MAPBOX_STREETS)
-
- verifyOrder {
- nativeMapView.removeLayer(layer1)
- nativeMapView.removeSource(source1)
- }
- }
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt
index 4f6e915d1f..fddf7eeaff 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt
@@ -25,8 +25,9 @@ class TransformTest {
every { nativeMapView.isDestroyed } returns false
every { nativeMapView.cameraPosition } returns CameraPosition.DEFAULT
every { nativeMapView.cancelTransitions() } answers {}
- every { nativeMapView.jumpTo(any(), any(), any(), any()) } answers {}
- every { nativeMapView.flyTo(any(), any(), any(), any(), any()) } answers {}
+ every { nativeMapView.jumpTo(any(), any(), any(), any(), any()) } answers {}
+ every { nativeMapView.easeTo(any(), any(), any(), any(), any(), any(), any()) } answers {}
+ every { nativeMapView.flyTo(any(), any(), any(), any(), any(), any()) } answers {}
every { nativeMapView.minZoom = any() } answers {}
every { nativeMapView.maxZoom = any() } answers {}
}
@@ -44,7 +45,103 @@ class TransformTest {
val update = CameraUpdateFactory.newCameraPosition(expected)
transform.moveCamera(mapboxMap, update, callback)
- verify { nativeMapView.jumpTo(target, -1.0, -1.0, -1.0) }
+ verify { nativeMapView.jumpTo(target, -1.0, -1.0, -1.0, null) }
+ verify { callback.onFinish() }
+ }
+
+ @Test
+ fun testMoveCameraToSamePosition() {
+ val mapboxMap = mockk<MapboxMap>()
+ every { mapboxMap.cameraPosition } answers { CameraPosition.DEFAULT }
+
+ val callback = mockk<MapboxMap.CancelableCallback>()
+ every { callback.onFinish() } answers {}
+
+ val expected = CameraPosition.DEFAULT
+ val update = CameraUpdateFactory.newCameraPosition(expected)
+
+ transform.moveCamera(mapboxMap, update, null) // Initialize camera position
+ transform.moveCamera(mapboxMap, update, callback)
+
+ verify(exactly = 1, verifyBlock = { nativeMapView.jumpTo(any(), any(), any(), any(), any()) })
+ verify { callback.onFinish() }
+ }
+
+ @Test
+ fun testEaseCamera() {
+ val mapboxMap = mockk<MapboxMap>()
+ every { mapboxMap.cameraPosition } answers { CameraPosition.DEFAULT }
+
+ every { mapView.addOnCameraDidChangeListener(any()) } answers { transform.onCameraDidChange(true) }
+ every { mapView.removeOnCameraDidChangeListener(any()) } answers {}
+
+ val callback = mockk<MapboxMap.CancelableCallback>()
+ every { callback.onFinish() } answers {}
+
+ val target = LatLng(1.0, 2.0)
+ val expected = CameraPosition.Builder().target(target).build()
+ val update = CameraUpdateFactory.newCameraPosition(expected)
+
+ transform.easeCamera(mapboxMap, update, 100, false, callback)
+
+ verify { nativeMapView.easeTo(target, -1.0, -1.0, -1.0, null, 100, false) }
+ verify { callback.onFinish() }
+ }
+
+ @Test
+ fun testEaseCameraToSamePosition() {
+ val mapboxMap = mockk<MapboxMap>()
+ every { mapboxMap.cameraPosition } answers { CameraPosition.DEFAULT }
+
+ val callback = mockk<MapboxMap.CancelableCallback>()
+ every { callback.onFinish() } answers {}
+
+ val expected = CameraPosition.DEFAULT
+ val update = CameraUpdateFactory.newCameraPosition(expected)
+ transform.moveCamera(mapboxMap, update, null)
+
+ transform.easeCamera(mapboxMap, update, 100, false, callback)
+
+ verify(exactly = 0, verifyBlock = { nativeMapView.easeTo(any(), any(), any(), any(), any(), any(), any()) })
+ verify { callback.onFinish() }
+ }
+
+ @Test
+ fun testAnimateCamera() {
+ val mapboxMap = mockk<MapboxMap>()
+ every { mapboxMap.cameraPosition } answers { CameraPosition.DEFAULT }
+
+ every { mapView.addOnCameraDidChangeListener(any()) } answers { transform.onCameraDidChange(true) }
+ every { mapView.removeOnCameraDidChangeListener(any()) } answers {}
+
+ val callback = mockk<MapboxMap.CancelableCallback>()
+ every { callback.onFinish() } answers {}
+
+ val target = LatLng(1.0, 2.0)
+ val expected = CameraPosition.Builder().target(target).build()
+ val update = CameraUpdateFactory.newCameraPosition(expected)
+
+ transform.animateCamera(mapboxMap, update, 100, callback)
+
+ verify { nativeMapView.flyTo(target, -1.0, -1.0, -1.0, null, 100) }
+ verify { callback.onFinish() }
+ }
+
+ @Test
+ fun testAnimateCameraToSamePosition() {
+ val mapboxMap = mockk<MapboxMap>()
+ every { mapboxMap.cameraPosition } answers { CameraPosition.DEFAULT }
+
+ val callback = mockk<MapboxMap.CancelableCallback>()
+ every { callback.onFinish() } answers {}
+
+ val expected = CameraPosition.DEFAULT
+ val update = CameraUpdateFactory.newCameraPosition(expected)
+ transform.moveCamera(mapboxMap, update, null)
+
+ transform.animateCamera(mapboxMap, update, 100, callback)
+
+ verify(exactly = 0, verifyBlock = { nativeMapView.flyTo(any(), any(), any(), any(), any(), any()) })
verify { callback.onFinish() }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
index 80ac2213ee..a84e93cd75 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
@@ -5,6 +5,7 @@ import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
import org.junit.Before;
@@ -17,6 +18,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
public class UiSettingsTest {
@@ -341,17 +343,17 @@ public class UiSettingsTest {
}
@Test
- public void testIncreaseRotateThresholdWhenScalingEnabled() {
- uiSettings.setIncreaseRotateThresholdWhenScaling(true);
- assertEquals("Rotate threshold increase should be enabled", true,
- uiSettings.isIncreaseRotateThresholdWhenScaling());
+ public void testDisableRotateWhenScalingEnabled() {
+ uiSettings.setDisableRotateWhenScaling(true);
+ assertEquals("Rotate disabling should be enabled", true,
+ uiSettings.isDisableRotateWhenScaling());
}
@Test
- public void testIncreaseRotateThresholdWhenScalingDisabled() {
- uiSettings.setIncreaseRotateThresholdWhenScaling(false);
- assertEquals("Rotate threshold increase should be disabled", false,
- uiSettings.isIncreaseRotateThresholdWhenScaling());
+ public void testDisableRotateWhenScalingDisabled() {
+ uiSettings.setDisableRotateWhenScaling(false);
+ assertEquals("Rotate disabling should be disabled", false,
+ uiSettings.isDisableRotateWhenScaling());
}
@Test
@@ -412,4 +414,20 @@ public class UiSettingsTest {
assertEquals("Zoom rate should be 0.83f", 0.83f,
uiSettings.getZoomRate(), 0);
}
+
+ @Test
+ public void testUpdateWhenCompassViewNotHidden() {
+ CameraPosition cameraPosition = new CameraPosition.Builder(CameraPosition.DEFAULT).bearing(24.0f).build();
+ when(compassView.isHidden()).thenReturn(false);
+ uiSettings.update(cameraPosition);
+ verify(compassView).update(-24.0f);
+ }
+
+ @Test
+ public void testUpdateWhenCompassViewHidden() {
+ CameraPosition cameraPosition = new CameraPosition.Builder(CameraPosition.DEFAULT).bearing(24.0f).build();
+ when(compassView.isHidden()).thenReturn(true);
+ uiSettings.update(cameraPosition);
+ verify(compassView).update(-24.0f);
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/http/HttpRequestUrlTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/http/HttpRequestUrlTest.kt
index 994a41938b..5db7b0b0f9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/http/HttpRequestUrlTest.kt
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/http/HttpRequestUrlTest.kt
@@ -20,14 +20,14 @@ class HttpRequestUrlTest {
@Test
fun testOfflineFlagMapboxCom() {
- val expected = "http://mapbox.com/path/of/no/return.pbf?sku=foobar&offline=true"
+ val expected = "http://mapbox.com/path/of/no/return.pbf?offline=true"
val actual = HttpRequestUrl.buildResourceUrl("mapbox.com", "http://mapbox.com/path/of/no/return.pbf", 0, true)
assertEquals(expected, actual)
}
@Test
fun testOfflineFlagMapboxCn() {
- val expected = "http://mapbox.cn/path/of/no/return.pbf?sku=foobar&offline=true"
+ val expected = "http://mapbox.cn/path/of/no/return.pbf?offline=true"
val actual = HttpRequestUrl.buildResourceUrl("mapbox.cn", "http://mapbox.cn/path/of/no/return.pbf", 0, true)
assertEquals(expected, actual)
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt
index 50b9626edc..f840e970d1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt
@@ -1,11 +1,15 @@
package com.mapbox.mapboxsdk.camera
+import android.graphics.PointF
+import android.support.test.annotation.UiThreadTest
import android.support.test.runner.AndroidJUnit4
import com.mapbox.mapboxsdk.geometry.LatLng
import com.mapbox.mapboxsdk.geometry.LatLngBounds
import com.mapbox.mapboxsdk.testapp.activity.BaseTest
import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity
+import org.junit.Assert
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@@ -17,127 +21,166 @@ class CameraUpdateFactoryTest : BaseTest() {
}
@Test
+ @UiThreadTest
fun testLatLngBoundsUntiltedUnrotated() {
- rule.runOnUiThread {
- mapboxMap.cameraPosition = CameraPosition.Builder()
- .target(LatLng(60.0, 24.0))
- .bearing(0.0)
- .tilt(0.0)
- .build()
-
- val bounds: LatLngBounds = LatLngBounds.Builder()
- .include(LatLng(62.0, 26.0))
- .include(LatLng(58.0, 22.0))
- .build()
-
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
-
- val cameraPosition = mapboxMap.cameraPosition
- assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
- assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
- assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1)
- assertEquals("zoom should match", 5.5, cameraPosition.zoom, 0.1)
- assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1)
- }
+ mapboxMap.cameraPosition = CameraPosition.Builder()
+ .target(LatLng(60.0, 24.0))
+ .bearing(0.0)
+ .tilt(0.0)
+ .build()
+
+ val bounds: LatLngBounds = LatLngBounds.Builder()
+ .include(LatLng(62.0, 26.0))
+ .include(LatLng(58.0, 22.0))
+ .build()
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
+
+ val cameraPosition = mapboxMap.cameraPosition
+ assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
+ assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
+ assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1)
+ assertEquals("zoom should match", 5.5, cameraPosition.zoom, 0.1)
+ assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1)
}
@Test
+ @UiThreadTest
fun testLatLngBoundsTilted() {
- rule.runOnUiThread {
- mapboxMap.cameraPosition = CameraPosition.Builder()
- .target(LatLng(60.0, 24.0))
- .bearing(0.0)
- .tilt(45.0)
- .build()
-
- val bounds: LatLngBounds = LatLngBounds.Builder()
- .include(LatLng(62.0, 26.0))
- .include(LatLng(58.0, 22.0))
- .build()
-
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
-
- val cameraPosition = mapboxMap.cameraPosition
- assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
- assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
- assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1)
- assertEquals("zoom should match", 6.0, cameraPosition.zoom, 0.1)
- assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1)
- }
+ mapboxMap.cameraPosition = CameraPosition.Builder()
+ .target(LatLng(60.0, 24.0))
+ .bearing(0.0)
+ .tilt(45.0)
+ .build()
+
+ val bounds: LatLngBounds = LatLngBounds.Builder()
+ .include(LatLng(62.0, 26.0))
+ .include(LatLng(58.0, 22.0))
+ .build()
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
+
+ val cameraPosition = mapboxMap.cameraPosition
+ assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
+ assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
+ assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1)
+ assertEquals("zoom should match", 6.0, cameraPosition.zoom, 0.1)
+ assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1)
}
@Test
+ @UiThreadTest
fun testLatLngBoundsRotated() {
- rule.runOnUiThread {
- mapboxMap.cameraPosition = CameraPosition.Builder()
- .target(LatLng(60.0, 24.0))
- .bearing(30.0)
- .tilt(0.0)
- .build()
-
- val bounds: LatLngBounds = LatLngBounds.Builder()
- .include(LatLng(62.0, 26.0))
- .include(LatLng(58.0, 22.0))
- .build()
-
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
-
- val cameraPosition = mapboxMap.cameraPosition
- assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
- assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
- assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
- assertEquals("zoom should match", 5.3, cameraPosition.zoom, 0.1)
- assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1)
- }
+ mapboxMap.cameraPosition = CameraPosition.Builder()
+ .target(LatLng(60.0, 24.0))
+ .bearing(30.0)
+ .tilt(0.0)
+ .build()
+
+ val bounds: LatLngBounds = LatLngBounds.Builder()
+ .include(LatLng(62.0, 26.0))
+ .include(LatLng(58.0, 22.0))
+ .build()
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
+
+ val cameraPosition = mapboxMap.cameraPosition
+ assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
+ assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
+ assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
+ assertEquals("zoom should match", 5.3, cameraPosition.zoom, 0.1)
+ assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1)
}
@Test
+ @UiThreadTest
fun testLatLngBoundsTiltedRotated() {
- rule.runOnUiThread {
- mapboxMap.cameraPosition = CameraPosition.Builder()
- .target(LatLng(60.0, 24.0))
- .bearing(30.0)
- .tilt(45.0)
- .build()
-
- val bounds: LatLngBounds = LatLngBounds.Builder()
- .include(LatLng(62.0, 26.0))
- .include(LatLng(58.0, 22.0))
- .build()
-
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
-
- val cameraPosition = mapboxMap.cameraPosition
- assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
- assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
- assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
- assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1)
- assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1)
- }
+ mapboxMap.cameraPosition = CameraPosition.Builder()
+ .target(LatLng(60.0, 24.0))
+ .bearing(30.0)
+ .tilt(45.0)
+ .build()
+
+ val bounds: LatLngBounds = LatLngBounds.Builder()
+ .include(LatLng(62.0, 26.0))
+ .include(LatLng(58.0, 22.0))
+ .build()
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
+
+ val cameraPosition = mapboxMap.cameraPosition
+ assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
+ assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
+ assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
+ assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1)
+ assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1)
}
@Test
+ @UiThreadTest
fun testLatLngBoundsWithProvidedTiltAndRotation() {
- rule.runOnUiThread {
- mapboxMap.cameraPosition = CameraPosition.Builder()
- .target(LatLng(60.0, 24.0))
- .bearing(0.0)
- .tilt(0.0)
- .build()
-
- val bounds: LatLngBounds = LatLngBounds.Builder()
- .include(LatLng(62.0, 26.0))
- .include(LatLng(58.0, 22.0))
- .build()
-
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30.0, 40.0, 0))
-
- val cameraPosition = mapboxMap.cameraPosition
- assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
- assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
- assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
- assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1)
- assertEquals("tilt should match:", 40.0, cameraPosition.tilt, 0.1)
- }
+ mapboxMap.cameraPosition = CameraPosition.Builder()
+ .target(LatLng(60.0, 24.0))
+ .bearing(0.0)
+ .tilt(0.0)
+ .build()
+
+ val bounds: LatLngBounds = LatLngBounds.Builder()
+ .include(LatLng(62.0, 26.0))
+ .include(LatLng(58.0, 22.0))
+ .build()
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30.0, 40.0, 0))
+
+ val cameraPosition = mapboxMap.cameraPosition
+ assertEquals("latitude should match:", 60.0, cameraPosition.target.latitude, 0.1)
+ assertEquals("longitude should match:", 24.0, cameraPosition.target.longitude, 0.1)
+ assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
+ assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1)
+ assertEquals("tilt should match:", 40.0, cameraPosition.tilt, 0.1)
+ }
+
+ @Test
+ @UiThreadTest
+ fun withPadding_cameraInvalidated_paddingPersisting() {
+ val initialCameraPosition = mapboxMap.cameraPosition
+ val initialPoint = mapboxMap.projection.toScreenLocation(initialCameraPosition.target)
+
+ val bottomPadding = mapView.height / 4
+ val leftPadding = mapView.width / 4
+ val padding = doubleArrayOf(leftPadding.toDouble(), 0.0, 0.0, bottomPadding.toDouble())
+ mapboxMap.moveCamera(CameraUpdateFactory.paddingTo(leftPadding.toDouble(), 0.0, 0.0, bottomPadding.toDouble()))
+
+ Assert.assertArrayEquals(intArrayOf(leftPadding, 0, 0, bottomPadding), mapboxMap.padding)
+
+ val resultingCameraPosition = mapboxMap.cameraPosition
+ assertEquals(initialCameraPosition.target, resultingCameraPosition.target)
+ assertEquals(
+ PointF(initialPoint.x + leftPadding / 2, initialPoint.y - bottomPadding / 2),
+ mapboxMap.projection.toScreenLocation(resultingCameraPosition.target)
+ )
+ Assert.assertArrayEquals(padding, resultingCameraPosition.padding, 0.0001)
+ }
+
+ @Test
+ @UiThreadTest
+ fun withLatLngPadding_cameraInvalidated_paddingPersisting() {
+ val expectedTarget = LatLng(2.0, 2.0)
+
+ val topPadding = mapView.height / 4
+ val rightPadding = mapView.width / 4
+ val padding = doubleArrayOf(0.0, topPadding.toDouble(), rightPadding.toDouble(), 0.0)
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngPadding(expectedTarget, 0.0, topPadding.toDouble(), rightPadding.toDouble(), 0.0))
+
+ Assert.assertArrayEquals(intArrayOf(0, topPadding, rightPadding, 0), mapboxMap.padding)
+
+ val resultingCameraPosition = mapboxMap.cameraPosition
+ assertEquals(expectedTarget.latitude, resultingCameraPosition.target.latitude, 0.1)
+ assertEquals(expectedTarget.longitude, resultingCameraPosition.target.longitude, 0.1)
+
+ val centerLatLng = mapboxMap.projection.fromScreenLocation(PointF((mapView.width / 2).toFloat(), (mapView.height / 2).toFloat()))
+ assertTrue(centerLatLng.latitude > resultingCameraPosition.target.latitude)
+ assertTrue(centerLatLng.longitude > resultingCameraPosition.target.longitude)
+ Assert.assertArrayEquals(padding, resultingCameraPosition.padding, 0.0001)
}
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
index fb113502ee..51c9e1c100 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
@@ -1289,6 +1289,36 @@ class LocationComponentTest : EspressoTest() {
}
@Test
+ fun animators_dontZoomWhileTransitioning() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(
+ component: LocationComponent,
+ mapboxMap: MapboxMap,
+ style: Style,
+ uiController: UiController,
+ context: Context
+ ) {
+ component.activateLocationComponent(LocationComponentActivationOptions
+ .builder(context, style)
+ .useDefaultLocationEngine(false)
+ .build())
+ component.isLocationComponentEnabled = true
+ component.forceLocationUpdate(location)
+
+ val zoom = mapboxMap.cameraPosition.zoom
+ component.setCameraMode(CameraMode.TRACKING_GPS, 500L, null, null, null, null)
+ component.zoomWhileTracking(16.0, 1000)
+ uiController.loopMainThreadForAtLeast(1000)
+ TestingAsyncUtils.waitForLayer(uiController, mapView)
+
+ assertEquals(zoom, mapboxMap.cameraPosition.zoom, 0.0001)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
@Ignore
fun animators_cancelZoomWhileTracking() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
@@ -1434,6 +1464,36 @@ class LocationComponentTest : EspressoTest() {
}
@Test
+ fun animators_dontTiltWhileTransitioning() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(
+ component: LocationComponent,
+ mapboxMap: MapboxMap,
+ style: Style,
+ uiController: UiController,
+ context: Context
+ ) {
+ component.activateLocationComponent(LocationComponentActivationOptions
+ .builder(context, style)
+ .useDefaultLocationEngine(false)
+ .build())
+ component.isLocationComponentEnabled = true
+ component.forceLocationUpdate(location)
+
+ val tilt = mapboxMap.cameraPosition.tilt
+ component.setCameraMode(CameraMode.TRACKING_GPS, 500L, null, null, null, null)
+ component.tiltWhileTracking(30.0, 1000)
+ uiController.loopMainThreadForAtLeast(1000)
+ TestingAsyncUtils.waitForLayer(uiController, mapView)
+
+ assertEquals(tilt, mapboxMap.cameraPosition.tilt, 0.0001)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
@Ignore
fun animators_cancelTiltWhileTracking() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt
index 3980199cad..525c576df4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt
@@ -176,4 +176,18 @@ class MapGestureDetectorTest : BaseTest() {
Assert.assertNotEquals(initialCameraPosition!!.target.longitude, mapboxMap.cameraPosition.target.longitude, 1.0)
}
}
+
+ @Test
+ fun quickZoom_roundTripping() {
+ validateTestSetup()
+ rule.runOnUiThread {
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(51.0, 16.0), 3.0))
+ }
+ onView(withId(R.id.mapView)).perform(quickScale(300f, withVelocity = false, duration = 750L))
+ onView(withId(R.id.mapView)).perform(quickScale(-300f, withVelocity = false, duration = 750L))
+
+ rule.runOnUiThread {
+ Assert.assertEquals(3.0, mapboxMap.cameraPosition.zoom, 0.0001)
+ }
+ }
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapInstrumentationTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapInstrumentationTest.kt
deleted file mode 100644
index e93f54161e..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapInstrumentationTest.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.mapbox.mapboxsdk.maps
-
-import android.graphics.PointF
-import android.support.test.runner.AndroidJUnit4
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
-import com.mapbox.mapboxsdk.geometry.LatLng
-import com.mapbox.mapboxsdk.testapp.activity.BaseTest
-import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity
-import org.junit.Assert.assertArrayEquals
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MapboxMapInstrumentationTest : BaseTest() {
-
- override fun getActivityClass(): Class<*> {
- return DeviceIndependentTestActivity::class.java
- }
-
- @Test
- fun setPadding_cameraInvalidated() {
- rule.runOnUiThread {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 10.0))
- val initialCameraPosition = mapboxMap.cameraPosition
- val initialPoint = mapboxMap.projection.toScreenLocation(initialCameraPosition.target)
-
- val bottomPadding = mapView.height / 4
- val leftPadding = mapView.width / 4
- mapboxMap.setPadding(leftPadding, 0, 0, bottomPadding)
-
- val resultingCameraPosition = mapboxMap.cameraPosition
- assertArrayEquals(intArrayOf(leftPadding, 0, 0, bottomPadding), mapboxMap.padding)
- assertEquals(initialCameraPosition, resultingCameraPosition)
- assertEquals(
- PointF(initialPoint.x + leftPadding / 2, initialPoint.y - bottomPadding / 2),
- mapboxMap.projection.toScreenLocation(resultingCameraPosition.target)
- )
- }
- }
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt
index d5834db553..b13bb6b796 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt
@@ -15,6 +15,7 @@ import com.mapbox.mapboxsdk.style.layers.TransitionOptions
import com.mapbox.mapboxsdk.testapp.utils.TestConstants
import junit.framework.Assert.*
import org.junit.After
+import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,6 +31,7 @@ class NativeMapViewTest : AppCenter() {
const val BEARING_TEST = 60.0
const val PITCH_TEST = 40.0
const val ZOOM_TEST = 16.0
+ val PADDING_TEST = doubleArrayOf(80.0, 150.0, 0.0, 0.0)
const val WIDTH = 500
const val HEIGHT = WIDTH
val LATLNG_TEST = LatLng(12.0, 34.0)
@@ -88,6 +90,18 @@ class NativeMapViewTest : AppCenter() {
@Test
@UiThreadTest
+ fun testLatLngPadding() {
+ val expected = LATLNG_TEST
+ nativeMapView.contentPadding = PADDING_TEST
+ nativeMapView.setLatLng(expected, 0)
+ val actual = nativeMapView.latLng
+ assertEquals("Latitude should match", expected.latitude, actual.latitude, DELTA)
+ assertEquals("Longitude should match", expected.longitude, actual.longitude, DELTA)
+ Assert.assertArrayEquals(PADDING_TEST, nativeMapView.cameraPosition.padding, DELTA)
+ }
+
+ @Test
+ @UiThreadTest
fun testLatLngDefault() {
val expected = LatLng()
val actual = nativeMapView.latLng
@@ -145,14 +159,18 @@ class NativeMapViewTest : AppCenter() {
.target(LATLNG_TEST)
.tilt(PITCH_TEST)
.zoom(ZOOM_TEST)
+ .padding(PADDING_TEST)
.build()
- nativeMapView.jumpTo(LATLNG_TEST, ZOOM_TEST, PITCH_TEST, BEARING_TEST)
+ // verify that the lazily set padding is ignored when a value is provided with the camera
+ nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
+ nativeMapView.jumpTo(LATLNG_TEST, ZOOM_TEST, PITCH_TEST, BEARING_TEST, PADDING_TEST)
val actual = nativeMapView.cameraPosition
assertEquals("Latitude should match", expected.target.latitude, actual.target.latitude, DELTA)
assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, DELTA)
assertEquals("Bearing should match", expected.bearing, actual.bearing, DELTA)
assertEquals("Pitch should match", expected.tilt, actual.tilt, DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, DELTA)
+ Assert.assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
@@ -206,7 +224,7 @@ class NativeMapViewTest : AppCenter() {
@Test
@UiThreadTest
fun testSetContentPadding() {
- val expected = floatArrayOf(1.0f, 2.0f, 3.0f, 4.0f)
+ val expected = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
nativeMapView.contentPadding = expected
val actual = nativeMapView.contentPadding
assertEquals("Left should match", expected[0], actual[0])
@@ -258,14 +276,18 @@ class NativeMapViewTest : AppCenter() {
.tilt(30.0)
.target(LatLng(12.0, 14.0))
.bearing(20.0)
+ .padding(PADDING_TEST)
.build()
- nativeMapView.flyTo(expected.target, expected.zoom, expected.bearing, expected.tilt, 0)
+ // verify that the lazily set padding is ignored when a value is provided with the camera
+ nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
+ nativeMapView.flyTo(expected.target, expected.zoom, expected.bearing, expected.tilt, PADDING_TEST, 0)
val actual = nativeMapView.cameraPosition
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
assertEquals("Latitude should match", expected.target.latitude, actual.target.latitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
+ Assert.assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
@@ -276,14 +298,18 @@ class NativeMapViewTest : AppCenter() {
.tilt(30.0)
.target(LatLng(12.0, 14.0))
.bearing(20.0)
+ .padding(PADDING_TEST)
.build()
- nativeMapView.easeTo(expected.target, expected.zoom, expected.bearing, expected.tilt, 0, false)
+ // verify that the lazily set padding is ignored when a value is provided with the camera
+ nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
+ nativeMapView.easeTo(expected.target, expected.zoom, expected.bearing, expected.tilt, PADDING_TEST, 0, false)
val actual = nativeMapView.cameraPosition
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
assertEquals("Latitude should match", expected.target.latitude, actual.target.latitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
+ Assert.assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
@@ -294,8 +320,9 @@ class NativeMapViewTest : AppCenter() {
.tilt(0.0)
.target(LatLng(0.0, 0.0))
.bearing(0.0)
+ .padding(PADDING_TEST)
.build()
- nativeMapView.jumpTo(LatLng(1.0, 2.0), 12.0, 23.0, 1.0)
+ nativeMapView.jumpTo(LatLng(1.0, 2.0), 12.0, 23.0, 1.0, PADDING_TEST)
nativeMapView.resetPosition()
val actual = nativeMapView.cameraPosition
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
@@ -303,6 +330,7 @@ class NativeMapViewTest : AppCenter() {
assertEquals("Longitude should match", expected.target.longitude, actual.target.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
+ Assert.assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt
index 14722ea37c..12dff5d452 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt
@@ -7,7 +7,8 @@ import com.mapbox.mapboxsdk.geometry.LatLng
import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke
import com.mapbox.mapboxsdk.testapp.activity.BaseTest
import com.mapbox.mapboxsdk.testapp.activity.espresso.PixelTestActivity
-import org.junit.Assert.*
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Test
class VisibleRegionTest : BaseTest() {
@@ -68,7 +69,7 @@ class VisibleRegionTest : BaseTest() {
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(1, filtered.size)
+ assertTrue(filtered.size == 1)
assertTrue(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)))
}
}
@@ -87,18 +88,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 4f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width * 3f / 4f, 0f)
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(mapView.width / 4, 0, 0, 0)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
- val filtered = latLngs.filter {
- visibleRegion.latLngBounds.contains(it)
- }
- assertEquals(5, filtered.size)
+ val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f)))
}
}
@@ -117,16 +114,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 4f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height * 3f / 4f)
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(0, mapView.height / 4, 0, 0)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(5, filtered.size)
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f)))
}
}
@@ -145,16 +140,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 4f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width * 3f / 4f, 0f)
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(0, 0, mapView.width / 4, 0)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(5, filtered.size)
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)))
}
}
@@ -173,16 +166,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 4f),
- mapboxMap.getLatLngFromScreenCoords(0f, mapView.height * 3f / 4f)
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(0, 0, 0, mapView.height / 4)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(5, filtered.size)
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat())))
}
}
@@ -236,7 +227,7 @@ class VisibleRegionTest : BaseTest() {
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(1, filtered.size)
+ assertTrue(filtered.size == 1)
assertTrue(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)))
}
}
@@ -258,17 +249,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 4f, 0f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width * 3f / 4f, mapView.height / 2f)
- .also { it.longitude += 360 }
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(mapView.width / 4, 0, 0, 0)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(5, filtered.size)
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f)))
}
}
@@ -278,6 +266,7 @@ class VisibleRegionTest : BaseTest() {
validateTestSetup()
invoke(mapboxMap) { ui: UiController, mapboxMap: MapboxMap ->
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
+ ui.loopMainThreadForAtLeast(5000)
val latLngs = listOf(
mapboxMap.getLatLngFromScreenCoords(0f, 0f),
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
@@ -290,17 +279,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 4f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height * 3f / 4f)
- .also { it.longitude += 360 }
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(0, mapView.height / 4, 0, 0)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(5, filtered.size)
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f)))
}
}
@@ -322,17 +308,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 4f, 0f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width * 3f / 4f, mapView.height / 2f)
- .also { it.longitude += 360 }
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(0, 0, mapView.width / 4, 0)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(5, filtered.size)
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)))
}
}
@@ -354,17 +337,14 @@ class VisibleRegionTest : BaseTest() {
mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
- mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 4f),
- mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height * 3f / 4f)
- .also { it.longitude += 360 }
+ mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
mapboxMap.setPadding(0, 0, 0, mapView.height / 4)
val visibleRegion = mapboxMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
- assertEquals(5, filtered.size)
+ assertTrue(filtered.size == 6)
assertFalse(filtered.contains(mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat())))
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/offline/OfflineDownloadTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/offline/OfflineDownloadTest.kt
index 93b59d6e33..6b5bc63adb 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/offline/OfflineDownloadTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/offline/OfflineDownloadTest.kt
@@ -26,42 +26,39 @@ class OfflineDownloadTest : OfflineRegion.OfflineRegionObserver {
private val countDownLatch = CountDownLatch(1)
private lateinit var offlineRegion: OfflineRegion
- @Test(timeout = 30000)
+ @Test(timeout = 60000)
fun offlineDownload() {
rule.runOnUiThreadActivity {
OfflineManager.getInstance(rule.activity).createOfflineRegion(
createTestRegionDefinition(),
ByteArray(0),
object : OfflineManager.CreateOfflineRegionCallback {
- override fun onCreate(region: OfflineRegion?) {
- region?.let {
- offlineRegion = it
- offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE)
- offlineRegion.setObserver(this@OfflineDownloadTest)
- }
+ override fun onCreate(region: OfflineRegion) {
+ offlineRegion = region
+ offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE)
+ offlineRegion.setObserver(this@OfflineDownloadTest)
}
- override fun onError(error: String?) {
+ override fun onError(error: String) {
Logger.e(TAG, "Error while creating offline region: $error")
}
})
}
- if (!countDownLatch.await(30, TimeUnit.SECONDS)) {
+ if (!countDownLatch.await(60, TimeUnit.SECONDS)) {
throw TimeoutException()
}
}
- override fun onStatusChanged(status: OfflineRegionStatus?) {
- status?.let {
- if (it.isComplete) {
- offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE)
- countDownLatch.countDown()
- }
+ override fun onStatusChanged(status: OfflineRegionStatus) {
+ Logger.i(TAG, "Download percentage ${100.0 * status.completedResourceCount / status.requiredResourceCount}")
+ if (status.isComplete) {
+ offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE)
+ countDownLatch.countDown()
}
}
- override fun onError(error: OfflineRegionError?) {
+ override fun onError(error: OfflineRegionError) {
Logger.e(TAG, "Error while downloading offline region: $error")
}
@@ -69,7 +66,7 @@ class OfflineDownloadTest : OfflineRegion.OfflineRegionObserver {
Logger.e(TAG, "Tile count limited exceeded: $limit")
}
- fun createTestRegionDefinition(): OfflineRegionDefinition {
+ private fun createTestRegionDefinition(): OfflineRegionDefinition {
return OfflineGeometryRegionDefinition(
Style.MAPBOX_STREETS,
Point.fromLngLat(50.847857, 4.360137),
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseTest.java
index 45dac23611..b6c16c8147 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseTest.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.support.annotation.CallSuper;
import android.support.annotation.UiThread;
import android.support.test.rule.ActivityTestRule;
+import android.support.test.rule.GrantPermissionRule;
import com.mapbox.mapboxsdk.AppCenter;
import com.mapbox.mapboxsdk.Mapbox;
@@ -35,6 +36,10 @@ public abstract class BaseTest extends AppCenter {
@Rule
public TestName testName = new TestName();
+ @Rule
+ public GrantPermissionRule grantLocationPermissionRule = GrantPermissionRule
+ .grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
+
protected MapboxMap mapboxMap;
protected MapView mapView;
private final CountDownLatch latch = new CountDownLatch(1);
@@ -75,7 +80,12 @@ public abstract class BaseTest extends AppCenter {
try {
rule.runOnUiThread(() -> {
mapView = rule.getActivity().findViewById(R.id.mapView);
- mapView.getMapAsync(this::initMap);
+ if (mapView != null) {
+ mapView.getMapAsync(this::initMap);
+ } else {
+ Timber.w("Skipping map load test since mapView is not found.");
+ latch.countDown();
+ }
});
} catch (Throwable throwable) {
throwable.printStackTrace();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java
index 625e37798f..69cf1264bf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java
@@ -1,297 +1,11 @@
package com.mapbox.mapboxsdk.testapp.camera;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.IdlingRegistry;
-import android.support.test.espresso.idling.CountingIdlingResource;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.testapp.activity.BaseTest;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.TestConstants;
-
-import org.junit.Test;
-
-import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
-import static org.junit.Assert.assertEquals;
-
-public class CameraAnimateTest extends BaseTest {
-
- private final CountingIdlingResource animationIdlingResource =
- new CountingIdlingResource("animation_idling_resource");
-
- @Override
- protected Class getActivityClass() {
- return DeviceIndependentTestActivity.class;
- }
+public class CameraAnimateTest extends CameraTest {
@Override
- public void beforeTest() {
- super.beforeTest();
- IdlingRegistry.getInstance().register(animationIdlingResource);
- }
-
- @Test
- public void testAnimateToCameraPositionTarget() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
- LatLng moveTarget = new LatLng(1, 1);
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
-
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testAnimateToCameraPositionTargetZoom() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
-
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom), new MapboxMap
- .CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testAnimateToCameraPosition() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(moveTarget)
- .zoom(moveZoom)
- .tilt(moveTilt)
- .bearing(moveBearing)
- .build()),
- new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testAnimateToBounds() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final LatLng centerBounds = new LatLng(1, 1);
- LatLng cornerOne = new LatLng();
- LatLng cornerTwo = new LatLng(2, 2);
- final LatLngBounds.Builder builder = new LatLngBounds.Builder();
- builder.include(cornerOne);
- builder.include(cornerTwo);
-
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0), new MapboxMap
- .CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
+ void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) {
+ mapboxMap.animateCamera(cameraUpdate, callback);
}
-
- @Test
- public void testAnimateToZoomIn() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
-
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testAnimateToZoomOut() {
- validateTestSetup();
- float zoom = 10.0f;
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- animationIdlingResource.increment();
- mapboxMap.animateCamera(
- CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom),
- new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- invoke(mapboxMap, ((uiController, mapboxMap1) -> {
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- }));
-
- Espresso.onIdle();
- }
-
- @Test
- public void testAnimateToZoomBy() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
-
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testAnimateToZoomTo() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final float zoomTo = 2.45f;
-
- animationIdlingResource.increment();
- mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Override
- public void afterTest() {
- super.afterTest();
- IdlingRegistry.getInstance().unregister(animationIdlingResource);
- }
-
- private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing,
- double moveTilt) {
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
- assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
- }
-}
-
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java
index ae81dc1302..be3b18ef8b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java
@@ -1,294 +1,12 @@
package com.mapbox.mapboxsdk.testapp.camera;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.IdlingRegistry;
-import android.support.test.espresso.idling.CountingIdlingResource;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.testapp.activity.BaseTest;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.TestConstants;
-
-import org.junit.Test;
-
-import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
-import static org.junit.Assert.assertEquals;
-
-public class CameraEaseTest extends BaseTest {
- private final CountingIdlingResource animationIdlingResource =
- new CountingIdlingResource("animation_idling_resource");
-
- @Override
- protected Class getActivityClass() {
- return DeviceIndependentTestActivity.class;
- }
+public class CameraEaseTest extends CameraTest {
@Override
- public void beforeTest() {
- super.beforeTest();
- IdlingRegistry.getInstance().register(animationIdlingResource);
- }
-
- @Test
- public void testEaseToCameraPositionTarget() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
- LatLng moveTarget = new LatLng(1, 1);
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
-
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testEaseToCameraPositionTargetZoom() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
-
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testEaseToCameraPosition() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(moveTarget)
- .zoom(moveZoom)
- .tilt(moveTilt)
- .bearing(moveBearing)
- .build()),
- new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt);
- animationIdlingResource.decrement();
- }
- }
- );
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testEaseToBounds() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final LatLng centerBounds = new LatLng(1, 1);
- LatLng cornerOne = new LatLng();
- LatLng cornerTwo = new LatLng(2, 2);
- final LatLngBounds.Builder builder = new LatLngBounds.Builder();
- builder.include(cornerOne);
- builder.include(cornerTwo);
-
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0),
- new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testEaseToZoomIn() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
-
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testEaseToZoomOut() {
- float zoom = 10.0f;
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testEaseToZoomBy() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
-
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testEaseToZoomTo() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final float zoomTo = 2.45f;
-
- animationIdlingResource.increment();
- mapboxMap.easeCamera(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Override
- public void afterTest() {
- super.afterTest();
- IdlingRegistry.getInstance().unregister(animationIdlingResource);
- }
-
- private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing,
- double moveTilt) {
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
- assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
+ void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) {
+ mapboxMap.easeCamera(cameraUpdate, callback);
}
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java
index f8aa43d52d..2cd7b6ffe3 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java
@@ -1,294 +1,12 @@
package com.mapbox.mapboxsdk.testapp.camera;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.IdlingRegistry;
-import android.support.test.espresso.idling.CountingIdlingResource;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.testapp.activity.BaseTest;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.TestConstants;
-
-import org.junit.Test;
-
-import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
-import static org.junit.Assert.assertEquals;
-
-public class CameraMoveTest extends BaseTest {
-
- private final CountingIdlingResource animationIdlingResource =
- new CountingIdlingResource("animation_idling_resource");
-
- @Override
- protected Class getActivityClass() {
- return DeviceIndependentTestActivity.class;
- }
-
- @Override
- public void beforeTest() {
- super.beforeTest();
- IdlingRegistry.getInstance().register(animationIdlingResource);
- }
-
- @Test
- public void testMoveToCameraPositionTarget() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
- LatLng moveTarget = new LatLng(1, 1);
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(zoom).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
-
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testMoveToCameraPositionTargetZoom() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
-
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testMoveToCameraPosition() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(moveTarget)
- .zoom(moveZoom)
- .tilt(moveTilt)
- .bearing(moveBearing)
- .build()),
- new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testMoveToBounds() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final LatLng centerBounds = new LatLng(1, 1);
- LatLng cornerOne = new LatLng();
- LatLng cornerTwo = new LatLng(2, 2);
- final LatLngBounds.Builder builder = new LatLngBounds.Builder();
- builder.include(cornerOne);
- builder.include(cornerTwo);
-
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 0),
- new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testMoveToZoomIn() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
-
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testMoveToZoomOut() {
- float zoom = 10.0f;
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testMoveToZoomBy() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
-
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
-
- @Test
- public void testMoveToZoomTo() {
- validateTestSetup();
- invoke(mapboxMap, (uiController, mapboxMap) -> {
- final float zoomTo = 2.45f;
-
- animationIdlingResource.increment();
- mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0);
- animationIdlingResource.decrement();
- }
-
- @Override
- public void onFinish() {
- verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0);
- animationIdlingResource.decrement();
- }
- });
- });
-
- Espresso.onIdle();
- }
+public class CameraMoveTest extends CameraTest {
@Override
- public void afterTest() {
- super.afterTest();
- IdlingRegistry.getInstance().unregister(animationIdlingResource);
- }
-
- private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing,
- double moveTilt) {
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
- moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
- moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
- assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
- assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
+ void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) {
+ mapboxMap.moveCamera(cameraUpdate, callback);
}
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java
new file mode 100644
index 0000000000..ebce9aaec5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java
@@ -0,0 +1,307 @@
+package com.mapbox.mapboxsdk.testapp.camera;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdate;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.testapp.activity.BaseTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.DeviceIndependentTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.TestConstants;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public abstract class CameraTest extends BaseTest {
+
+ private CountDownLatch latch;
+
+ @Override
+ protected Class getActivityClass() {
+ return DeviceIndependentTestActivity.class;
+ }
+
+ @Override
+ public void beforeTest() {
+ super.beforeTest();
+ latch = new CountDownLatch(1);
+ }
+
+ @Test
+ public void testToCameraPositionTarget() throws InterruptedException {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ LatLng moveTarget = new LatLng(1, 1);
+ CameraPosition initialPosition = new CameraPosition.Builder().target(
+ new LatLng()).zoom(zoom).bearing(0).tilt(0).padding(new double[4]).build();
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Default camera position should match default", cameraPosition, initialPosition);
+
+ executeCameraMovement(CameraUpdateFactory.newLatLng(moveTarget), new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ @Test
+ public void testToCameraPositionTargetZoom() throws InterruptedException {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float moveZoom = 15.5f;
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+
+ executeCameraMovement(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom),
+ new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ @Test
+ public void testToCameraPosition() throws InterruptedException {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
+ final float moveZoom = 15.5f;
+ final float moveTilt = 45.5f;
+ final float moveBearing = 12.5f;
+ final double[] movePadding = new double[] {0, 500, 350, 1};
+
+ executeCameraMovement(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(moveTarget)
+ .zoom(moveZoom)
+ .tilt(moveTilt)
+ .bearing(moveBearing)
+ .padding(movePadding)
+ .build()),
+ new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt, movePadding);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt, movePadding);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ @Test
+ public void testToBounds() throws InterruptedException {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final LatLng centerBounds = new LatLng(1, 1);
+ LatLng cornerOne = new LatLng();
+ LatLng cornerTwo = new LatLng(2, 2);
+ final LatLngBounds.Builder builder = new LatLngBounds.Builder();
+ builder.include(cornerOne);
+ builder.include(cornerTwo);
+
+ executeCameraMovement(CameraUpdateFactory.newLatLngBounds(builder.build(), 0),
+ new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ @Test
+ public void testToZoomIn() throws InterruptedException {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+
+ executeCameraMovement(CameraUpdateFactory.zoomIn(), new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ @Test
+ public void testToZoomOut() throws InterruptedException {
+ float zoom = 10.0f;
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ executeCameraMovement(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom), new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+
+ latch = new CountDownLatch(1);
+
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ executeCameraMovement(CameraUpdateFactory.zoomOut(), new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ @Test
+ public void testToZoomBy() throws InterruptedException {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ float zoom = 1.0f;
+ final float zoomBy = 2.45f;
+
+ executeCameraMovement(CameraUpdateFactory.zoomBy(zoomBy), new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ @Test
+ public void testToZoomTo() throws InterruptedException {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float zoomTo = 2.45f;
+
+ executeCameraMovement(CameraUpdateFactory.zoomTo(zoomTo), new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0, new double[4]);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFinish() {
+ verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0, new double[4]);
+ latch.countDown();
+ }
+ });
+ });
+
+ if (!latch.await(10, TimeUnit.SECONDS)) {
+ Assert.fail("timeout");
+ }
+ }
+
+ abstract void executeCameraMovement(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback);
+
+ private void verifyCameraPosition(MapboxMap mapboxMap, LatLng moveTarget, double moveZoom, double moveBearing,
+ double moveTilt, double[] padding) {
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
+ moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
+ moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
+ assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
+ assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
+ assertArrayEquals("Moved padding should match", cameraPosition.padding, padding, TestConstants.PADDING_DELTA);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt
index a6238ebf14..9c2eb3df81 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.style
import android.support.test.espresso.Espresso.onView
import android.support.test.espresso.matcher.ViewMatchers.isRoot
-import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource
import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource.THREAD_POOL_LIMIT
import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource.THREAD_PREFIX
import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke
@@ -15,7 +14,6 @@ import com.mapbox.mapboxsdk.testapp.activity.style.GridSourceActivity.ID_GRID_LA
import com.mapbox.mapboxsdk.testapp.activity.style.GridSourceActivity.ID_GRID_SOURCE
import com.mapbox.mapboxsdk.testapp.utils.TestingAsyncUtils
import org.junit.Assert
-import org.junit.Ignore
import org.junit.Test
class CustomGeometrySourceTest : BaseTest() {
@@ -23,7 +21,6 @@ class CustomGeometrySourceTest : BaseTest() {
override fun getActivityClass(): Class<*> = GridSourceActivity::class.java
@Test
- @Ignore
fun sourceNotLeakingThreadsTest() {
validateTestSetup()
WaitAction.invoke(4000)
@@ -38,7 +35,6 @@ class CustomGeometrySourceTest : BaseTest() {
}
@Test
- @Ignore
fun threadsShutdownWhenSourceRemovedTest() {
validateTestSetup()
invoke(mapboxMap) { uiController, mapboxMap ->
@@ -48,13 +44,12 @@ class CustomGeometrySourceTest : BaseTest() {
TestingAsyncUtils.waitForLayer(uiController, mapView)
Assert.assertTrue("There should be no threads running when the source is removed.",
Thread.getAllStackTraces().keys.filter {
- it.name.startsWith(CustomGeometrySource.THREAD_PREFIX)
+ it.name.startsWith(THREAD_PREFIX)
}.count() == 0)
}
}
@Test
- @Ignore
fun threadsRestartedWhenSourceReAddedTest() {
validateTestSetup()
invoke(mapboxMap) { uiController, mapboxMap ->
@@ -67,8 +62,8 @@ class CustomGeometrySourceTest : BaseTest() {
TestingAsyncUtils.waitForLayer(uiController, mapView)
Assert.assertTrue("Threads should be restarted when the source is re-added to the map.",
Thread.getAllStackTraces().keys.filter {
- it.name.startsWith(CustomGeometrySource.THREAD_PREFIX)
- }.count() == CustomGeometrySource.THREAD_POOL_LIMIT)
+ it.name.startsWith(THREAD_PREFIX)
+ }.count() == THREAD_POOL_LIMIT)
}
}
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java
index fbb866ddff..95bd651cff 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java
@@ -25,13 +25,19 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
+import java.util.HashMap;
import timber.log.Timber;
import static com.mapbox.mapboxsdk.style.expressions.Expression.FormatOption.formatFontScale;
import static com.mapbox.mapboxsdk.style.expressions.Expression.FormatOption.formatTextColor;
import static com.mapbox.mapboxsdk.style.expressions.Expression.FormatOption.formatTextFont;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.NumberFormatOption.currency;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.NumberFormatOption.locale;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.NumberFormatOption.maxFractionDigits;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.NumberFormatOption.minFractionDigits;
import static com.mapbox.mapboxsdk.style.expressions.Expression.collator;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.format;
@@ -41,6 +47,7 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
import static com.mapbox.mapboxsdk.style.expressions.Expression.number;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.numberFormat;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
import static com.mapbox.mapboxsdk.style.expressions.Expression.step;
@@ -56,8 +63,10 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOutlineColor
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@RunWith(AndroidJUnit4.class)
@@ -561,6 +570,197 @@ public class ExpressionTest extends EspressoTest {
});
}
+ @Test
+ public void testNumberFormatCurrencyExpression() {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLng = new LatLng(51, 17);
+ mapboxMap.getStyle()
+ .addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
+ SymbolLayer layer = new SymbolLayer("layer", "source");
+ mapboxMap.getStyle().addLayer(layer);
+
+ layer.setProperties(
+ textField(
+ numberFormat(12.345, locale("en-US"), currency("USD"))
+ )
+ );
+ TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
+
+ assertFalse(mapboxMap.queryRenderedFeatures(
+ mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
+ );
+ assertNull(layer.getTextField().getExpression());
+ assertEquals("$12.35", layer.getTextField().getValue().getFormattedSections()[0].getText());
+ });
+ }
+
+ @Test
+ public void testNumberFormatMaxExpression() {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLng = new LatLng(51, 17);
+ mapboxMap.getStyle()
+ .addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
+ SymbolLayer layer = new SymbolLayer("layer", "source");
+ mapboxMap.getStyle().addLayer(layer);
+
+ layer.setProperties(
+ textField(
+ numberFormat(12.34567890, maxFractionDigits(5), minFractionDigits(0))
+ )
+ );
+ TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
+
+ assertFalse(mapboxMap.queryRenderedFeatures(
+ mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
+ );
+ assertNull(layer.getTextField().getExpression());
+ assertEquals("12.34568", layer.getTextField().getValue().getFormattedSections()[0].getText());
+ });
+ }
+
+ @Test
+ public void testNumberFormatMinExpression() {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLng = new LatLng(51, 17);
+ mapboxMap.getStyle()
+ .addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
+ SymbolLayer layer = new SymbolLayer("layer", "source");
+ mapboxMap.getStyle().addLayer(layer);
+
+ layer.setProperties(
+ textField(
+ numberFormat(12.0000001, maxFractionDigits(5), minFractionDigits(0))
+ )
+ );
+ TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
+
+ assertFalse(mapboxMap.queryRenderedFeatures(
+ mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
+ );
+ assertNull(layer.getTextField().getExpression());
+ assertEquals("12", layer.getTextField().getValue().getFormattedSections()[0].getText());
+ });
+ }
+
+ @Test
+ public void testNumberFormatLocaleExpression() {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLng = new LatLng(51, 17);
+ mapboxMap.getStyle()
+ .addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
+ SymbolLayer layer = new SymbolLayer("layer", "source");
+ mapboxMap.getStyle().addLayer(layer);
+
+ layer.setProperties(
+ textField(
+ numberFormat(12.0000001, locale("nl-BE"), maxFractionDigits(5), minFractionDigits(1))
+ )
+ );
+ TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
+
+ assertFalse(mapboxMap.queryRenderedFeatures(
+ mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
+ );
+ assertNull(layer.getTextField().getExpression());
+ assertEquals("12,0", layer.getTextField().getValue().getFormattedSections()[0].getText());
+ });
+ }
+
+ @Test
+ public void testNumberFormatNonConstantExpression() {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLng = new LatLng(51, 17);
+ Feature feature = Feature.fromGeometry(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()));
+ feature.addNumberProperty("number_value", 12.345678);
+ feature.addStringProperty("locale_value", "nl-BE");
+ feature.addNumberProperty("max_value", 5);
+ feature.addNumberProperty("min_value", 1);
+
+
+ mapboxMap.getStyle().addSource(new GeoJsonSource("source", feature));
+ SymbolLayer layer = new SymbolLayer("layer", "source");
+ mapboxMap.getStyle().addLayer(layer);
+
+ Expression numberFormatExpression = numberFormat(
+ number(number(get("number_value"))),
+ locale(string(get("locale_value"))),
+ maxFractionDigits(number(get("max_value"))),
+ minFractionDigits(number(get("min_value")))
+ );
+
+ layer.setProperties(textField(numberFormatExpression));
+ TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
+
+ assertFalse(mapboxMap.queryRenderedFeatures(
+ mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
+ );
+
+ assertNotNull(layer.getTextField().getExpression());
+
+ // Expressions evaluated to string are wrapped by a format expression, take array index 1 to get original
+ Object[] returnExpression = (Object[]) layer.getTextField().getExpression().toArray()[1];
+ Object[] setExpression = numberFormatExpression.toArray();
+ assertEquals("Number format should match",returnExpression[0], setExpression[0]);
+ assertArrayEquals("Get value expression should match",
+ (Object[]) returnExpression[1],
+ (Object[]) setExpression[1]
+ );
+
+ // number format objects
+ HashMap<String, Object> returnMap = (HashMap<String, Object>) returnExpression[2];
+ HashMap<String, Object> setMap = (HashMap<String, Object>) returnExpression[2];
+
+ assertArrayEquals("Number format min fraction digits should match ",
+ (Object[]) returnMap.get("min-fraction-digits"),
+ (Object[]) setMap.get("min-fraction-digits")
+ );
+
+ assertArrayEquals("Number format max fraction digits should match ",
+ (Object[]) returnMap.get("max-fraction-digits"),
+ (Object[]) setMap.get("max-fraction-digits")
+ );
+
+ assertArrayEquals("Number format min fraction digits should match ",
+ (Object[]) returnMap.get("locale"),
+ (Object[]) setMap.get("locale")
+ );
+ });
+
+ }
+
+ /**
+ * Regression test for #15532
+ */
+ @Test
+ public void testDoubleConversion() {
+ validateTestSetup();
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ LatLng latLng = new LatLng(51, 17);
+ mapboxMap.getStyle().addSource(
+ new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()))
+ );
+
+ CircleLayer layer = new CircleLayer("layer", "source");
+ mapboxMap.getStyle().addLayer(layer);
+
+ Expression input = interpolate(
+ exponential(0.5f), zoom(),
+ stop(-0.1, color(Color.RED)),
+ stop(0, color(Color.BLUE))
+ );
+
+ layer.setProperties(circleColor(input));
+
+ Expression output = layer.getCircleColor().getExpression();
+ assertArrayEquals("Expression should match", input.toArray(), output.toArray());
+ });
+ }
+
private void setupStyle() {
invoke(mapboxMap, (uiController, mapboxMap) -> {
// Add a source
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 60e305d607..c65e37019b 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
@@ -670,6 +670,19 @@ public class SymbolLayerTest extends BaseLayerTest {
@Test
@UiThreadTest
+ public void testTextWritingModeAsConstant() {
+ Timber.i("text-writing-mode");
+ assertNotNull(layer);
+ assertNull(layer.getTextWritingMode().getValue());
+
+ // Set and Get
+ String[] propertyValue = new String[0];
+ layer.setProperties(textWritingMode(propertyValue));
+ assertEquals(layer.getTextWritingMode().getValue(), propertyValue);
+ }
+
+ @Test
+ @UiThreadTest
public void testTextRotateAsConstant() {
Timber.i("text-rotate");
assertNotNull(layer);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java
index 09e27d30f2..255d1e1e7d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java
@@ -5,6 +5,7 @@ public class TestConstants {
public static final double BEARING_DELTA = 0.1;
public static final double TILT_DELTA = 0.3;
public static final double ZOOM_DELTA = 0.3;
+ public static final double PADDING_DELTA = 0.0001;
public static final String TEXT_MARKER_TITLE = "Marker";
public static final String TEXT_MARKER_SNIPPET = "Snippet";
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/FontUtilsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/FontUtilsTest.java
new file mode 100644
index 0000000000..fa068cb973
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/FontUtilsTest.java
@@ -0,0 +1,41 @@
+package com.mapbox.mapboxsdk.utils;
+
+import android.support.test.runner.AndroidJUnit4;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+@RunWith(AndroidJUnit4.class)
+public class FontUtilsTest {
+
+ @Test
+ public void testExtractedFontShouldMatchDefault() {
+ String[] fonts = new String[] {"foo", "bar"};
+ String actual = FontUtils.extractValidFont(fonts);
+ assertEquals("Selected font should match", MapboxConstants.DEFAULT_FONT, actual);
+ }
+
+ @Test
+ public void testExtractedFontShouldMatchMonospace() {
+ String expected = "monospace";
+ String[] fonts = new String[] {"foo", expected};
+ String actual = FontUtils.extractValidFont(fonts);
+ assertEquals("Selected font should match", expected, actual);
+ }
+
+ @Test
+ public void testExtractedFontArrayShouldBeNull() {
+ String[] fonts = null;
+ String actual = FontUtils.extractValidFont(fonts);
+ assertNull(actual);
+ }
+
+ @Test
+ public void testExtractedFontShouldBeNull() {
+ String actual = FontUtils.extractValidFont(null);
+ assertNull(actual);
+ }
+}
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 05337081dc..d8fd428762 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
@@ -151,6 +151,9 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
@Override
protected void onDestroy() {
super.onDestroy();
+ if (progressDialog != null) {
+ progressDialog.dismiss();
+ }
mapView.onDestroy();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
index e278d033be..7ce54831f2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
@@ -36,6 +36,7 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
private static final LatLng START_LAT_LNG = new LatLng(37.787947, -122.407432);
private final LongSparseArray<Animator> animators = new LongSparseArray<>();
+ private Animator set;
{
AnimatorSet accelerateDecelerateAnimatorSet = new AnimatorSet();
@@ -92,7 +93,8 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
.bearing(135)
.build();
- createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition).start();
+ set = createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition);
+ set.start();
});
}
@@ -237,6 +239,9 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
for (int i = 0; i < animators.size(); i++) {
animators.get(animators.keyAt(i)).cancel();
}
+ if (set != null) {
+ set.cancel();
+ }
}
@Override
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 83b0c415ec..6979097658 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
@@ -63,12 +63,12 @@ public class CameraPositionActivity extends FragmentActivity implements OnMapRea
fab = findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary));
fab.setOnClickListener(this);
- });
- toggleLogCameraChanges();
+ toggleLogCameraChanges();
- // listen to long click events to toggle logging camera changes
- mapboxMap.addOnMapLongClickListener(this);
+ // listen to long click events to toggle logging camera changes
+ mapboxMap.addOnMapLongClickListener(this);
+ });
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
index 40ea7509b1..a0e8b31fcd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
@@ -81,7 +81,9 @@ public class MapFragmentActivity extends AppCompatActivity implements MapFragmen
@Override
protected void onDestroy() {
super.onDestroy();
- mapView.removeOnDidFinishRenderingFrameListener(this);
+ if (mapView != null) {
+ mapView.removeOnDidFinishRenderingFrameListener(this);
+ }
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt
index ff2afb50c5..7e18cbfa2f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt
@@ -116,7 +116,7 @@ class LocationFragmentActivity : AppCompatActivity() {
}
override fun onSuccess(result: LocationEngineResult?) {
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(result?.lastLocation), 12.0))
+ if (!mapView.isDestroyed) mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(result?.lastLocation), 12.0))
}
override fun onFailure(exception: Exception) {
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 b34cdc6698..097ef316fe 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
@@ -25,7 +25,6 @@ import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.utils.IdleZoomListener;
import java.util.List;
import java.util.Locale;
@@ -41,9 +40,9 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
private MapView mapView;
private MapboxMap mapboxMap;
+ private MapboxMap.OnCameraMoveListener cameraMoveListener;
private ActionBarDrawerToggle actionBarDrawerToggle;
private int currentStyleIndex;
- private IdleZoomListener idleZoomListener;
private boolean isReportFps = true;
private static final String[] STYLES = new String[] {
@@ -152,7 +151,13 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
private void setupZoomView() {
final TextView textView = findViewById(R.id.textZoom);
- mapboxMap.addOnCameraIdleListener(idleZoomListener = new IdleZoomListener(mapboxMap, textView));
+ mapboxMap.addOnCameraMoveListener(cameraMoveListener = new MapboxMap.OnCameraMoveListener() {
+ @Override
+ public void onCameraMove() {
+ textView.setText(String.format(DebugModeActivity.this.getString(
+ R.string.debug_zoom), mapboxMap.getCameraPosition().zoom));
+ }
+ });
}
private void setupDebugChangeView() {
@@ -233,8 +238,8 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
@Override
protected void onDestroy() {
super.onDestroy();
- if (mapboxMap != null && idleZoomListener != null) {
- mapboxMap.removeOnCameraIdleListener(idleZoomListener);
+ if (mapboxMap != null) {
+ mapboxMap.removeOnCameraMoveListener(cameraMoveListener);
}
mapView.onDestroy();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
index 74fdf3d751..59c708266e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
@@ -7,6 +7,7 @@ import android.widget.GridLayout;
import android.widget.ImageView;
import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
@@ -68,7 +69,7 @@ public class MapSnapshotterActivity extends AppCompatActivity {
// Optionally the style
.withStyle((column + row) % 2 == 0 ? Style.MAPBOX_STREETS : Style.DARK)
- .withLocalIdeographFontFamily("sans-serif");
+ .withLocalIdeographFontFamily(MapboxConstants.DEFAULT_FONT);
// Optionally the visible region
if (row % 2 == 0) {
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 732a7929b8..cb2701d436 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
@@ -30,6 +30,8 @@ import java.util.List;
import java.util.Objects;
import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.accumulated;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.concat;
import static com.mapbox.mapboxsdk.style.expressions.Expression.division;
import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
@@ -39,6 +41,8 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.has;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.max;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.neq;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
@@ -138,6 +142,9 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
.withCluster(true)
.withClusterMaxZoom(14)
.withClusterRadius(50)
+ .withClusterProperty("max", max(accumulated(), get("max")), get("mag"))
+ .withClusterProperty("sum", literal("+"), get("mag"))
+ .withClusterProperty("felt", literal("any"), neq(get("felt"), literal("null")))
);
}
@@ -182,9 +189,9 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
}
private SymbolLayer createClusterTextLayer() {
- return new SymbolLayer("count", "earthquakes")
+ return new SymbolLayer("property", "earthquakes")
.withProperties(
- textField(Expression.toString(get("point_count"))),
+ textField(concat(get("point_count"), literal(", "), get("max"))),
textSize(12f),
textColor(Color.WHITE),
textIgnorePlacement(true),
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
index 24bfe62e75..e80d6fe3e1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
@@ -362,7 +362,12 @@ public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapR
@Override
protected void onPostExecute(HashMap<String, Bitmap> bitmapHashMap) {
super.onPostExecute(bitmapHashMap);
- mapboxMap.getStyle().addImagesAsync(bitmapHashMap);
+ mapboxMap.getStyle(new Style.OnStyleLoaded() {
+ @Override
+ public void onStyleLoaded(@NonNull Style style) {
+ style.addImagesAsync(bitmapHashMap);
+ }
+ });
}
}
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java
index 3d65a92f72..ee562ad6e8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java
@@ -38,12 +38,15 @@ import java.util.Random;
import static com.mapbox.mapboxsdk.style.expressions.Expression.FormatOption.formatFontScale;
import static com.mapbox.mapboxsdk.style.expressions.Expression.FormatOption.formatTextColor;
import static com.mapbox.mapboxsdk.style.expressions.Expression.FormatOption.formatTextFont;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.NumberFormatOption.currency;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.NumberFormatOption.locale;
import static com.mapbox.mapboxsdk.style.expressions.Expression.concat;
import static com.mapbox.mapboxsdk.style.expressions.Expression.format;
import static com.mapbox.mapboxsdk.style.expressions.Expression.formatEntry;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.numberFormat;
import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase;
@@ -72,8 +75,6 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize;
*/
public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.OnMapClickListener, OnMapReadyCallback {
- private static final String MARKER_SOURCE = "marker-source";
- private static final String MARKER_LAYER = "marker-layer";
private static final String ID_FEATURE_PROPERTY = "id";
private static final String SELECTED_FEATURE_PROPERTY = "selected";
private static final String TITLE_FEATURE_PROPERTY = "title";
@@ -81,8 +82,13 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.
private static final String[] NORMAL_FONT_STACK = new String[] {"DIN Offc Pro Regular", "Arial Unicode MS Regular"};
private static final String[] BOLD_FONT_STACK = new String[] {"DIN Offc Pro Bold", "Arial Unicode MS Regular"};
+ // layer & source constants
+ private static final String MARKER_SOURCE = "marker-source";
+ private static final String MARKER_LAYER = "marker-layer";
private static final String MAPBOX_SIGN_SOURCE = "mapbox-sign-source";
private static final String MAPBOX_SIGN_LAYER = "mapbox-sign-layer";
+ private static final String NUMBER_FORMAT_SOURCE = "mapbox-number-source";
+ private static final String NUMBER_FORMAT_LAYER = "mapbox-number-layer";
private static final Expression TEXT_FIELD_EXPRESSION =
switchCase(toBool(get(SELECTED_FEATURE_PROPERTY)),
@@ -108,6 +114,7 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.
private FeatureCollection markerCollection;
private SymbolLayer markerSymbolLayer;
private SymbolLayer mapboxSignSymbolLayer;
+ private SymbolLayer numberFormatSymbolLayer;
private MapboxMap mapboxMap;
private MapView mapView;
@@ -177,11 +184,20 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.
mapboxSignSymbolLayer = new SymbolLayer(MAPBOX_SIGN_LAYER, MAPBOX_SIGN_SOURCE);
shuffleMapboxSign();
+ // number format layer
+ Source numberFormatSource = new GeoJsonSource(NUMBER_FORMAT_SOURCE, Point.fromLngLat(4.92756, 52.3516));
+ numberFormatSymbolLayer = new SymbolLayer(NUMBER_FORMAT_LAYER, NUMBER_FORMAT_SOURCE);
+ numberFormatSymbolLayer.setProperties(
+ textField(
+ numberFormat(123.456789, locale("nl-NL"), currency("EUR"))
+ )
+ );
+
mapboxMap.setStyle(new Style.Builder()
.fromUri("asset://streets.json")
.withImage("Car", Objects.requireNonNull(carBitmap), false)
- .withSources(markerSource, mapboxSignSource)
- .withLayers(markerSymbolLayer, mapboxSignSymbolLayer)
+ .withSources(markerSource, mapboxSignSource, numberFormatSource)
+ .withLayers(markerSymbolLayer, mapboxSignSymbolLayer, numberFormatSymbolLayer)
);
// Set a click-listener so we can manipulate the map
@@ -205,7 +221,7 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.
// validate symbol flicker regression for #13407
markerSymbolLayer.setProperties(iconOpacity(match(
get(ID_FEATURE_PROPERTY), literal(1.0f),
- stop(feature.getStringProperty("id"), selected ? 0.3f : 1.0f)
+ stop(feature.getStringProperty("id"), selected ? 0.3f : 1.0f)
)));
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_backstack_fragment.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_backstack_fragment.xml
index 10c11a9320..b2ba18872d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_backstack_fragment.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_backstack_fragment.xml
@@ -9,6 +9,7 @@
<Button android:layout_width="match_parent"
android:layout_height="58dp"
+ android:layout_marginTop="64dp"
android:id="@+id/button"
android:contentDescription="btn_change_fragment"
android:text="Replace with empty fragment"/>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml
index 28bdaf7b2a..9b57e18a7a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml
@@ -18,6 +18,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?android:attr/actionBarSize"
+ app:mapbox_localIdeographFontFamilies="@array/array_local_ideograph_family_test"
app:mapbox_uiAttributionTintColor="@color/redAccent"/>
<android.support.design.widget.FloatingActionButton
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml
index 26c61e1639..58e49c3cef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml
@@ -10,6 +10,7 @@
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ app:mapbox_localIdeographEnabled="false"
app:mapbox_cameraTargetLat="51.50325"
app:mapbox_cameraTargetLng="-0.11968"
app:mapbox_cameraZoom="15" />
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml
index 4b7ded8e3a..ad9f88a46e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml
@@ -7,4 +7,9 @@
<item>1000</item>
<item>10000</item>
</string-array>
+ <string-array name="array_local_ideograph_family_test">
+ <item>foo</item>
+ <item>monospace</item>
+ <item>bar</item>
+ </string-array>
</resources> \ No newline at end of file
diff --git a/platform/android/build.gradle b/platform/android/build.gradle
index 46666e2ef1..607958a8a8 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.2'
+ classpath 'com.android.tools.build:gradle:3.5.0'
classpath dependenciesList.licensesPlugin
classpath dependenciesList.kotlinPlugin
classpath dependenciesList.bintrayPlugin
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index 4bd628fcdd..98d18ca324 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -43,7 +43,7 @@ macro(mbgl_platform_core)
)
target_link_libraries(mbgl-core
- PUBLIC jni.hpp
+ PUBLIC Mapbox::Base::jni.hpp
PUBLIC -llog
PUBLIC -landroid
PUBLIC -ljnigraphics
@@ -61,7 +61,7 @@ macro(mbgl_filesource)
target_link_libraries(mbgl-filesource
PUBLIC sqlite
- PUBLIC jni.hpp
+ PUBLIC Mapbox::Base::jni.hpp
PUBLIC -llog
PUBLIC -landroid
PUBLIC -latomic
diff --git a/platform/android/core-files.json b/platform/android/core-files.json
index 62ecc0c671..b19e5d9a93 100644
--- a/platform/android/core-files.json
+++ b/platform/android/core-files.json
@@ -38,6 +38,7 @@
"platform/android/src/java/util.cpp",
"platform/android/src/java_types.cpp",
"platform/android/src/jni.cpp",
+ "platform/android/src/jni_native.cpp",
"platform/android/src/logger.cpp",
"platform/android/src/logging_android.cpp",
"platform/android/src/map/camera_position.cpp",
@@ -142,6 +143,7 @@
"java/util.hpp": "platform/android/src/java/util.hpp",
"java_types.hpp": "platform/android/src/java_types.hpp",
"jni.hpp": "platform/android/src/jni.hpp",
+ "jni_native.hpp": "platform/android/src/jni_native.hpp",
"logger.hpp": "platform/android/src/logger.hpp",
"map/camera_position.hpp": "platform/android/src/map/camera_position.hpp",
"map/image.hpp": "platform/android/src/map/image.hpp",
diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle
index 0bd5a67c9c..c1c4894727 100644
--- a/platform/android/gradle/dependencies.gradle
+++ b/platform/android/gradle/dependencies.gradle
@@ -7,10 +7,10 @@ ext {
]
versions = [
- mapboxServices : '4.8.0',
+ mapboxServices : '4.9.0-alpha.1',
mapboxTelemetry : '4.5.1',
mapboxCore : '1.3.0',
- mapboxGestures : '0.4.2',
+ mapboxGestures : '0.5.1',
mapboxAccounts : '0.2.0',
supportLib : '28.0.0',
constraintLayout: '1.1.2',
diff --git a/platform/android/gradle/jacoco-report.gradle b/platform/android/gradle/jacoco-report.gradle
index c4e0bb9131..64c99e09f0 100644
--- a/platform/android/gradle/jacoco-report.gradle
+++ b/platform/android/gradle/jacoco-report.gradle
@@ -15,7 +15,7 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
}
def fileExcludes = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
- def debugTree = fileTree(dir: "${project.buildDir}/intermediates/javac/debug/compileDebugJavaWithJavac/classes", excludes: fileExcludes)
+ def debugTree = fileTree(dir: "${project.buildDir}/intermediates/javac/debug/classes", excludes: fileExcludes)
def mainSrc = "${project.projectDir}/src/main/java"
println(mainSrc)
def ecSrc = fileTree(dir: "$project.buildDir", include: "**/*.ec")
diff --git a/platform/android/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties
index a60d82ac08..efceced3ca 100644
--- a/platform/android/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json
index 0f5bb81b55..ab84957e34 100644
--- a/platform/android/scripts/exclude-activity-gen.json
+++ b/platform/android/scripts/exclude-activity-gen.json
@@ -5,7 +5,6 @@
"MapSnapshotterLocalStyleActivity",
"LatLngBoundsActivity",
"BottomSheetActivity",
- "MapSnapshotterActivity",
"MockLocationEngine",
"DeleteRegionActivity",
"RealTimeGeoJsonActivity",
@@ -50,7 +49,5 @@
"EspressoTestActivity",
"FragmentBackStackActivity",
"ChildFragmentMapInDialogActivity",
- "PerformanceMeasurementActivity",
- "DownloadRegionActivity",
- "CacheManagementActivity"
+ "PerformanceMeasurementActivity"
]
diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js
index 8c34113de1..fa814f89af 100755
--- a/platform/android/scripts/generate-style-code.js
+++ b/platform/android/scripts/generate-style-code.js
@@ -49,10 +49,19 @@ var layers = Object.keys(spec.layer.type.values).map((type) => {
});
// Process all layer properties
+const uniqueArrayEnum = (prop, enums) => {
+ if (prop.value !== 'enum') return false;
+ const enumsEqual = (val1, val2) => val1.length === val1.length && val1.every((val, i) => val === val2[i]);
+ return enums.filter(e => enumsEqual(Object.keys(prop.values).sort(), Object.keys(e.values).sort())).length == 0;
+};
+
const layoutProperties = _(layers).map('layoutProperties').flatten().value();
const paintProperties = _(layers).map('paintProperties').flatten().value();
const allProperties = _(layoutProperties).union(paintProperties).union(lightProperties).value();
-const enumProperties = _(allProperties).filter({'type': 'enum'}).value();
+let allEnumProperties = _(allProperties).filter({'type': 'enum'}).value();
+const uniqueArrayEnumProperties = _(allProperties).filter({'type': 'array'}).filter(prop => uniqueArrayEnum(prop, allEnumProperties)).value();
+const enumProperties = _(allEnumProperties).union(uniqueArrayEnumProperties).value();
+
global.propertyType = function propertyType(property) {
switch (property.type) {
diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp
index 6862fabcb4..d1a2624f0d 100644
--- a/platform/android/src/android_renderer_frontend.cpp
+++ b/platform/android/src/android_renderer_frontend.cpp
@@ -42,8 +42,8 @@ public:
delegate.invoke(&RendererObserver::onWillStartRenderingFrame);
}
- void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) override {
- delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded);
+ void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded, bool placementChanged) override {
+ delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded, placementChanged);
}
void onDidFinishRenderingMap() override {
diff --git a/platform/android/src/java_types.cpp b/platform/android/src/java_types.cpp
index dd165470cf..7a1ba93a58 100644
--- a/platform/android/src/java_types.cpp
+++ b/platform/android/src/java_types.cpp
@@ -18,6 +18,10 @@ namespace java {
jni::jclass* Map::jclass;
jni::jmethodID* Map::getMethodId;
+ jni::jmethodID* Map::keySetMethodId;
+
+ jni::jclass* Set::jclass;
+ jni::jmethodID* Set::toArrayMethodId;
void registerNatives(JNIEnv& env) {
ObjectArray::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "[Ljava/lang/Object;")).release();
@@ -34,6 +38,10 @@ namespace java {
Map::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Map")).release();
Map::getMethodId = &jni::GetMethodID(env, *Map::jclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ Map::keySetMethodId = &jni::GetMethodID(env, *Map::jclass, "keySet", "()Ljava/util/Set;");
+
+ Set::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Set")).release();
+ Set::toArrayMethodId = &jni::GetMethodID(env, *Set::jclass, "toArray", "()[Ljava/lang/Object;");
}
}
diff --git a/platform/android/src/java_types.hpp b/platform/android/src/java_types.hpp
index edec5cb550..c7c93ce71b 100644
--- a/platform/android/src/java_types.hpp
+++ b/platform/android/src/java_types.hpp
@@ -29,6 +29,12 @@ namespace java {
struct Map {
static jni::jclass* jclass;
static jni::jmethodID* getMethodId;
+ static jni::jmethodID* keySetMethodId;
+ };
+
+ struct Set {
+ static jni::jclass* jclass;
+ static jni::jmethodID* toArrayMethodId;
};
void registerNatives(JNIEnv&);
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index 088b3b796c..189ec29dce 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -2,66 +2,9 @@
#include <mbgl/util/logging.hpp>
-#include "annotation/marker.hpp"
-#include "annotation/polygon.hpp"
-#include "annotation/polyline.hpp"
-#include "bitmap.hpp"
-#include "bitmap_factory.hpp"
-#include "connectivity_listener.hpp"
-#include "conversion/conversion.hpp"
-#include "conversion/collection.hpp"
-#include "file_source.hpp"
-#include "geojson/feature.hpp"
-#include "geojson/feature_collection.hpp"
-#include "geojson/geometry.hpp"
-#include "geojson/geometry_collection.hpp"
-#include "geojson/line_string.hpp"
-#include "geojson/multi_line_string.hpp"
-#include "geojson/multi_point.hpp"
-#include "geojson/multi_polygon.hpp"
-#include "geojson/point.hpp"
-#include "geojson/polygon.hpp"
-#include "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"
-#include "gson/json_array.hpp"
-#include "gson/json_element.hpp"
-#include "gson/json_object.hpp"
-#include "gson/json_primitive.hpp"
-#include "java_types.hpp"
-#include "map_renderer.hpp"
-#include "map_renderer_runnable.hpp"
-#include "native_map_view.hpp"
-#ifndef MBGL_MODULE_OFFLINE_DISABLE
-#include "offline/offline_manager.hpp"
-#include "offline/offline_region.hpp"
-#include "offline/offline_region_definition.hpp"
-#include "offline/offline_region_error.hpp"
-#include "offline/offline_region_status.hpp"
-#endif
-#include "style/transition_options.hpp"
-#include "style/layers/layer_manager.hpp"
-#include "style/sources/source.hpp"
-#include "style/light.hpp"
-#include "style/formatted.hpp"
-#include "style/formatted_section.hpp"
-#ifndef MBGL_MODULE_SNAPSHOT_DISABLE
-#include "snapshotter/map_snapshotter.hpp"
-#include "snapshotter/map_snapshot.hpp"
-#endif
-#include "text/collator_jni.hpp"
-#include "text/local_glyph_rasterizer_jni.hpp"
-#include "text/format_number_jni.hpp"
-#include "logger.hpp"
-
namespace mbgl {
namespace android {
-void RegisterNativeHTTPRequest(JNIEnv&);
-
JavaVM* theJVM;
//TODO: remove
@@ -107,105 +50,5 @@ void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach) {
*env = nullptr;
}
-void registerNatives(JavaVM *vm) {
- theJVM = vm;
-
- jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6);
-
- // For the DefaultFileSource
- static mbgl::util::RunLoop mainRunLoop;
- FileSource::registerNative(env);
-
- // Basic types
- java::registerNatives(env);
- java::util::registerNative(env);
- PointF::registerNative(env);
- RectF::registerNative(env);
-
- // GeoJSON
- geojson::Feature::registerNative(env);
- geojson::FeatureCollection::registerNative(env);
- geojson::Geometry::registerNative(env);
- geojson::GeometryCollection::registerNative(env);
- geojson::LineString::registerNative(env);
- geojson::MultiLineString::registerNative(env);
- geojson::MultiPoint::registerNative(env);
- geojson::MultiPolygon::registerNative(env);
- geojson::Point::registerNative(env);
- geojson::Polygon::registerNative(env);
-
- // Geometry
- LatLng::registerNative(env);
- LatLngBounds::registerNative(env);
- LatLngQuad::registerNative(env);
- ProjectedMeters::registerNative(env);
-
- // GSon
- gson::JsonArray::registerNative(env);
- gson::JsonElement::registerNative(env);
- gson::JsonObject::registerNative(env);
- gson::JsonPrimitive::registerNative(env);
-
- //Annotation
- Marker::registerNative(env);
- Polygon::registerNative(env);
- Polyline::registerNative(env);
-
- // Map
- MapRenderer::registerNative(env);
- MapRendererRunnable::registerNative(env);
- NativeMapView::registerNative(env);
-
- // Http
- RegisterNativeHTTPRequest(env);
-
- // Bitmap
- Bitmap::registerNative(env);
- BitmapFactory::registerNative(env);
-
- // Style
- TransitionOptions::registerNative(env);
- LayerManagerAndroid::get()->registerNative(env);
- Source::registerNative(env);
- Light::registerNative(env);
- Position::registerNative(env);
- Formatted::registerNative(env);
- FormattedSection::registerNative(env);
-
- // Map
- CameraPosition::registerNative(env);
- Image::registerNative(env);
-
- // Connectivity
- ConnectivityListener::registerNative(env);
-
- // Offline
-#ifndef MBGL_MODULE_OFFLINE_DISABLE
- OfflineManager::registerNative(env);
- OfflineRegion::registerNative(env);
- OfflineRegionDefinition::registerNative(env);
- OfflineTilePyramidRegionDefinition::registerNative(env);
- OfflineGeometryRegionDefinition::registerNative(env);
- OfflineRegionError::registerNative(env);
- OfflineRegionStatus::registerNative(env);
-#endif
-
- // Snapshotter
-#ifndef MBGL_MODULE_SNAPSHOT_DISABLE
- MapSnapshotter::registerNative(env);
- MapSnapshot::registerNative(env);
-#endif
-
- // text
- LocalGlyphRasterizer::registerNative(env);
- Locale::registerNative(env);
- Collator::registerNative(env);
- StringUtils::registerNative(env);
- NumberFormat::registerNative(env);
-
- // Logger
- Logger::registerNative(env);
-}
-
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp
index e5df92e701..9bfe2ce5b7 100644
--- a/platform/android/src/jni.hpp
+++ b/platform/android/src/jni.hpp
@@ -1,6 +1,9 @@
#pragma once
+#include <mbgl/util/util.hpp>
+
#include <string>
+#include <jni/jni.hpp>
typedef struct _jmethodID* jmethodID;
typedef struct _JavaVM JavaVM;
@@ -17,7 +20,5 @@ extern std::string dataPath;
bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName);
void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);
-extern void registerNatives(JavaVM* vm);
-
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/jni_native.cpp b/platform/android/src/jni_native.cpp
new file mode 100755
index 0000000000..df96ba9759
--- /dev/null
+++ b/platform/android/src/jni_native.cpp
@@ -0,0 +1,164 @@
+#include "jni_native.hpp"
+
+#include "annotation/marker.hpp"
+#include "annotation/polygon.hpp"
+#include "annotation/polyline.hpp"
+#include "bitmap.hpp"
+#include "bitmap_factory.hpp"
+#include "connectivity_listener.hpp"
+#include "conversion/conversion.hpp"
+#include "conversion/collection.hpp"
+#include "file_source.hpp"
+#include "geojson/feature.hpp"
+#include "geojson/feature_collection.hpp"
+#include "geojson/geometry.hpp"
+#include "geojson/geometry_collection.hpp"
+#include "geojson/line_string.hpp"
+#include "geojson/multi_line_string.hpp"
+#include "geojson/multi_point.hpp"
+#include "geojson/multi_polygon.hpp"
+#include "geojson/point.hpp"
+#include "geojson/polygon.hpp"
+#include "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"
+#include "gson/json_array.hpp"
+#include "gson/json_element.hpp"
+#include "gson/json_object.hpp"
+#include "gson/json_primitive.hpp"
+#include "java_types.hpp"
+#include "map_renderer.hpp"
+#include "map_renderer_runnable.hpp"
+#include "native_map_view.hpp"
+#ifndef MBGL_MODULE_OFFLINE_DISABLE
+#include "offline/offline_manager.hpp"
+#include "offline/offline_region.hpp"
+#include "offline/offline_region_definition.hpp"
+#include "offline/offline_region_error.hpp"
+#include "offline/offline_region_status.hpp"
+#endif
+#include "style/transition_options.hpp"
+#include "style/layers/layer_manager.hpp"
+#include "style/sources/source.hpp"
+#include "style/light.hpp"
+#include "style/formatted.hpp"
+#include "style/formatted_section.hpp"
+#ifndef MBGL_MODULE_SNAPSHOT_DISABLE
+#include "snapshotter/map_snapshotter.hpp"
+#include "snapshotter/map_snapshot.hpp"
+#endif
+#include "text/collator_jni.hpp"
+#include "text/local_glyph_rasterizer_jni.hpp"
+#include "text/format_number_jni.hpp"
+#include "logger.hpp"
+
+namespace mbgl {
+namespace android {
+
+void RegisterNativeHTTPRequest(JNIEnv&);
+
+void registerNatives(JavaVM *vm) {
+ theJVM = vm;
+
+ jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6);
+
+ // For the DefaultFileSource
+ static mbgl::util::RunLoop mainRunLoop;
+ FileSource::registerNative(env);
+
+ // Basic types
+ java::registerNatives(env);
+ java::util::registerNative(env);
+ PointF::registerNative(env);
+ RectF::registerNative(env);
+
+ // GeoJSON
+ geojson::Feature::registerNative(env);
+ geojson::FeatureCollection::registerNative(env);
+ geojson::Geometry::registerNative(env);
+ geojson::GeometryCollection::registerNative(env);
+ geojson::LineString::registerNative(env);
+ geojson::MultiLineString::registerNative(env);
+ geojson::MultiPoint::registerNative(env);
+ geojson::MultiPolygon::registerNative(env);
+ geojson::Point::registerNative(env);
+ geojson::Polygon::registerNative(env);
+
+ // Geometry
+ LatLng::registerNative(env);
+ LatLngBounds::registerNative(env);
+ LatLngQuad::registerNative(env);
+ ProjectedMeters::registerNative(env);
+
+ // GSon
+ gson::JsonArray::registerNative(env);
+ gson::JsonElement::registerNative(env);
+ gson::JsonObject::registerNative(env);
+ gson::JsonPrimitive::registerNative(env);
+
+ //Annotation
+ Marker::registerNative(env);
+ Polygon::registerNative(env);
+ Polyline::registerNative(env);
+
+ // Map
+ MapRenderer::registerNative(env);
+ MapRendererRunnable::registerNative(env);
+ NativeMapView::registerNative(env);
+
+ // Http
+ RegisterNativeHTTPRequest(env);
+
+ // Bitmap
+ Bitmap::registerNative(env);
+ BitmapFactory::registerNative(env);
+
+ // Style
+ TransitionOptions::registerNative(env);
+ LayerManagerAndroid::get()->registerNative(env);
+ Source::registerNative(env);
+ Light::registerNative(env);
+ Position::registerNative(env);
+ Formatted::registerNative(env);
+ FormattedSection::registerNative(env);
+
+ // Map
+ CameraPosition::registerNative(env);
+ Image::registerNative(env);
+
+ // Connectivity
+ ConnectivityListener::registerNative(env);
+
+ // Offline
+#ifndef MBGL_MODULE_OFFLINE_DISABLE
+ OfflineManager::registerNative(env);
+ OfflineRegion::registerNative(env);
+ OfflineRegionDefinition::registerNative(env);
+ OfflineTilePyramidRegionDefinition::registerNative(env);
+ OfflineGeometryRegionDefinition::registerNative(env);
+ OfflineRegionError::registerNative(env);
+ OfflineRegionStatus::registerNative(env);
+#endif
+
+ // Snapshotter
+#ifndef MBGL_MODULE_SNAPSHOT_DISABLE
+ MapSnapshotter::registerNative(env);
+ MapSnapshot::registerNative(env);
+#endif
+
+ // text
+ LocalGlyphRasterizer::registerNative(env);
+ Locale::registerNative(env);
+ Collator::registerNative(env);
+ StringUtils::registerNative(env);
+ NumberFormat::registerNative(env);
+
+ // Logger
+ Logger::registerNative(env);
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/jni_native.hpp b/platform/android/src/jni_native.hpp
new file mode 100644
index 0000000000..1cc9a7e81b
--- /dev/null
+++ b/platform/android/src/jni_native.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <mbgl/util/util.hpp>
+
+#include "jni.hpp"
+
+namespace mbgl {
+namespace android {
+
+MBGL_EXPORT void registerNatives(JavaVM* vm);
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/main.cpp b/platform/android/src/main.cpp
index 03a8288719..9b5c6c1974 100644
--- a/platform/android/src/main.cpp
+++ b/platform/android/src/main.cpp
@@ -1,4 +1,5 @@
#include "jni.hpp"
+#include "jni_native.hpp"
#include <jni/jni.hpp>
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
@@ -6,4 +7,3 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
mbgl::android::registerNatives(vm);
return JNI_VERSION_1_6;
}
-
diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp
index fcbec5639a..cbf5f2e408 100644
--- a/platform/android/src/map/camera_position.cpp
+++ b/platform/android/src/map/camera_position.cpp
@@ -4,9 +4,9 @@
namespace mbgl {
namespace android {
-jni::Local<jni::Object<CameraPosition>> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOptions options) {
+jni::Local<jni::Object<CameraPosition>> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOptions options, float pixelRatio) {
static auto& javaClass = jni::Class<CameraPosition>::Singleton(env);
- static auto constructor = javaClass.GetConstructor<jni::Object<LatLng>, double, double, double>(env);
+ static auto constructor = javaClass.GetConstructor<jni::Object<LatLng>, double, double, double, jni::Array<jni::jdouble>>(env);
// wrap LatLng values coming from core
auto center = options.center.value();
@@ -25,21 +25,33 @@ jni::Local<jni::Object<CameraPosition>> CameraPosition::New(jni::JNIEnv &env, mb
// convert tilt, core ranges from [0 rad, 1,0472 rad], android ranges from 0 to 60
double tilt_degrees = options.pitch.value_or(0);
- return javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees);
+ std::vector<jdouble> paddingVect;
+ auto insets = options.padding.value_or(EdgeInsets {0, 0, 0, 0});
+ auto padding = jni::Array<jni::jdouble>::New(env, 4);
+ paddingVect.push_back(insets.left() * pixelRatio);
+ paddingVect.push_back(insets.top() * pixelRatio);
+ paddingVect.push_back(insets.right() * pixelRatio);
+ paddingVect.push_back(insets.bottom() * pixelRatio);
+ padding.SetRegion<std::vector<jni::jdouble>>(env, 0, paddingVect);
+
+ return javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees, padding);
}
-mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, const jni::Object<CameraPosition>& position) {
+mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, const jni::Object<CameraPosition>& position, float pixelRatio) {
static auto& javaClass = jni::Class<CameraPosition>::Singleton(env);
static auto bearing = javaClass.GetField<jni::jdouble>(env, "bearing");
static auto target = javaClass.GetField<jni::Object<LatLng>>(env, "target");
static auto tilt = javaClass.GetField<jni::jdouble>(env, "tilt");
static auto zoom = javaClass.GetField<jni::jdouble>(env, "zoom");
+ static auto paddingField = javaClass.GetField<jni::Array<jni::jdouble>>(env, "padding");
+ static auto padding = position.Get(env, paddingField);
auto center = LatLng::getLatLng(env, position.Get(env, target));
return mbgl::CameraOptions {
center,
- {},
+ padding ? EdgeInsets {padding.Get(env, 1) * pixelRatio, padding.Get(env, 0) * pixelRatio,
+ padding.Get(env, 3) * pixelRatio, padding.Get(env, 2) * pixelRatio} : (EdgeInsets) {},
{},
position.Get(env, zoom),
position.Get(env, bearing),
diff --git a/platform/android/src/map/camera_position.hpp b/platform/android/src/map/camera_position.hpp
index 7579f9fed1..b677f04ea0 100644
--- a/platform/android/src/map/camera_position.hpp
+++ b/platform/android/src/map/camera_position.hpp
@@ -12,9 +12,9 @@ class CameraPosition : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/camera/CameraPosition"; };
- static jni::Local<jni::Object<CameraPosition>> New(jni::JNIEnv&, mbgl::CameraOptions);
+ static jni::Local<jni::Object<CameraPosition>> New(jni::JNIEnv&, mbgl::CameraOptions, float pixelRatio);
- static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, const jni::Object<CameraPosition>&);
+ static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, const jni::Object<CameraPosition>&, float pixelRatio);
static void registerNative(jni::JNIEnv&);
};
diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp
index 92444c404d..6be708b994 100644
--- a/platform/android/src/map_renderer.cpp
+++ b/platform/android/src/map_renderer.cpp
@@ -16,11 +16,9 @@ namespace android {
MapRenderer::MapRenderer(jni::JNIEnv& _env,
const jni::Object<MapRenderer>& obj,
jni::jfloat pixelRatio_,
- const jni::String& programCacheDir_,
const jni::String& localIdeographFontFamily_)
: javaPeer(_env, obj)
, pixelRatio(pixelRatio_)
- , programCacheDir(jni::Make<std::string>(_env, programCacheDir_))
, localIdeographFontFamily(localIdeographFontFamily_ ? jni::Make<std::string>(_env, localIdeographFontFamily_) : optional<std::string>{})
, mailbox(std::make_shared<Mailbox>(*this)) {
}
@@ -173,7 +171,7 @@ void MapRenderer::onSurfaceCreated(JNIEnv&) {
// Create the new backend and renderer
backend = std::make_unique<AndroidRendererBackend>();
- renderer = std::make_unique<Renderer>(*backend, pixelRatio, programCacheDir, localIdeographFontFamily);
+ renderer = std::make_unique<Renderer>(*backend, pixelRatio, localIdeographFontFamily);
rendererRef = std::make_unique<ActorRef<Renderer>>(*renderer, mailbox);
// Set the observer on the new Renderer implementation
@@ -214,7 +212,7 @@ void MapRenderer::registerNative(jni::JNIEnv& env) {
// Register the peer
jni::RegisterNativePeer<MapRenderer>(env, javaClass, "nativePtr",
- jni::MakePeer<MapRenderer, const jni::Object<MapRenderer>&, jni::jfloat, const jni::String&, const jni::String&>,
+ jni::MakePeer<MapRenderer, const jni::Object<MapRenderer>&, jni::jfloat, const jni::String&>,
"nativeInitialize", "finalize",
METHOD(&MapRenderer::render, "nativeRender"),
METHOD(&MapRenderer::onRendererReset, "nativeReset"),
diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp
index efbe55af7f..5a8ddeeb91 100644
--- a/platform/android/src/map_renderer.hpp
+++ b/platform/android/src/map_renderer.hpp
@@ -44,7 +44,6 @@ public:
MapRenderer(jni::JNIEnv& _env,
const jni::Object<MapRenderer>&,
jni::jfloat pixelRatio,
- const jni::String& programCacheDir,
const jni::String& localIdeographFontFamily);
~MapRenderer() override;
@@ -104,7 +103,6 @@ private:
jni::WeakReference<jni::Object<MapRenderer>, jni::EnvAttachingDeleter> javaPeer;
float pixelRatio;
- std::string programCacheDir;
optional<std::string> localIdeographFontFamily;
std::shared_ptr<ThreadPool> threadPool;
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 47f2c6d9aa..7b87693cf5 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -185,7 +185,7 @@ void NativeMapView::onWillStartRenderingFrame() {
}
}
-void NativeMapView::onDidFinishRenderingFrame(MapObserver::RenderMode mode) {
+void NativeMapView::onDidFinishRenderingFrame(MapObserver::RenderFrameStatus status) {
assert(vm != nullptr);
android::UniqueEnv _env = android::AttachEnv();
@@ -193,7 +193,7 @@ void NativeMapView::onDidFinishRenderingFrame(MapObserver::RenderMode mode) {
static auto onDidFinishRenderingFrame = javaClass.GetMethod<void (jboolean)>(*_env, "onDidFinishRenderingFrame");
auto weakReference = javaPeer.get(*_env);
if (weakReference) {
- weakReference.Call(*_env, onDidFinishRenderingFrame, (jboolean) (mode != MapObserver::RenderMode::Partial));
+ weakReference.Call(*_env, onDidFinishRenderingFrame, (jboolean) (status.mode != MapObserver::RenderMode::Partial));
}
}
@@ -336,13 +336,17 @@ void NativeMapView::moveBy(jni::JNIEnv&, jni::jdouble dx, jni::jdouble dy, jni::
map->moveBy({dx, dy}, animationOptions);
}
-void NativeMapView::jumpTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jdouble pitch, jni::jdouble zoom) {
+void NativeMapView::jumpTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jdouble pitch, jni::jdouble zoom, const jni::Array<jni::jdouble>& padding) {
mbgl::CameraOptions options;
if (bearing != -1) {
options.bearing = bearing;
}
options.center = mbgl::LatLng(latitude, longitude);
- options.padding = insets;
+ if (padding) {
+ assert(padding.Length(env) == 4);
+ options.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1),
+ padding.Get(env, 2), padding.Get(env, 3)};
+ }
if (pitch != -1) {
options.pitch = pitch;
}
@@ -353,13 +357,17 @@ void NativeMapView::jumpTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble lati
map->jumpTo(options);
}
-void NativeMapView::easeTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, jni::jboolean easing) {
+void NativeMapView::easeTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, const jni::Array<jni::jdouble>& padding, jni::jboolean easing) {
mbgl::CameraOptions cameraOptions;
if (bearing != -1) {
cameraOptions.bearing = bearing;
}
cameraOptions.center = mbgl::LatLng(latitude, longitude);
- cameraOptions.padding = insets;
+ if (padding) {
+ assert(padding.Length(env) == 4);
+ cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1),
+ padding.Get(env, 2), padding.Get(env, 3)};
+ }
if (pitch != -1) {
cameraOptions.pitch = pitch;
}
@@ -377,13 +385,17 @@ void NativeMapView::easeTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble lati
map->easeTo(cameraOptions, animationOptions);
}
-void NativeMapView::flyTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom) {
+void NativeMapView::flyTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, const jni::Array<jni::jdouble>& padding) {
mbgl::CameraOptions cameraOptions;
if (bearing != -1) {
cameraOptions.bearing = bearing;
}
cameraOptions.center = mbgl::LatLng(latitude, longitude);
- cameraOptions.padding = insets;
+ if (padding) {
+ assert(padding.Length(env) == 4);
+ cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1),
+ padding.Get(env, 2), padding.Get(env, 3)};
+ }
if (pitch != -1) {
cameraOptions.pitch = pitch;
}
@@ -397,23 +409,29 @@ void NativeMapView::flyTo(jni::JNIEnv&, jni::jdouble bearing, jni::jdouble latit
}
jni::Local<jni::Object<LatLng>> NativeMapView::getLatLng(JNIEnv& env) {
- return LatLng::New(env, *map->getCameraOptions(insets).center);
+ return LatLng::New(env, *map->getCameraOptions(mbgl::nullopt).center);
}
-void NativeMapView::setLatLng(jni::JNIEnv&, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration) {
- map->easeTo(mbgl::CameraOptions().withCenter(mbgl::LatLng(latitude, longitude)).withPadding(insets),
- mbgl::AnimationOptions{mbgl::Milliseconds(duration)});
+void NativeMapView::setLatLng(jni::JNIEnv& env, jni::jdouble latitude, jni::jdouble longitude, const jni::Array<jni::jdouble>& padding, jni::jlong duration) {
+ mbgl::CameraOptions cameraOptions;
+ cameraOptions.center = mbgl::LatLng(latitude, longitude);
+ if (padding) {
+ assert(padding.Length(env) == 4);
+ cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1),
+ padding.Get(env, 2), padding.Get(env, 3)};
+ }
+ map->easeTo(cameraOptions, mbgl::AnimationOptions{mbgl::Milliseconds(duration)});
}
jni::Local<jni::Object<CameraPosition>> NativeMapView::getCameraForLatLngBounds(jni::JNIEnv& env, const jni::Object<LatLngBounds>& jBounds, double top, double left, double bottom, double right, double bearing, double tilt) {
mbgl::EdgeInsets padding = {top, left, bottom, right};
- return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), padding, bearing, tilt));
+ return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), padding, bearing, tilt), pixelRatio);
}
jni::Local<jni::Object<CameraPosition>> NativeMapView::getCameraForGeometry(jni::JNIEnv& env, const jni::Object<geojson::Geometry>& jGeometry, double top, double left, double bottom, double right, double bearing, double tilt) {
auto geometry = geojson::Geometry::convert(env, jGeometry);
mbgl::EdgeInsets padding = {top, left, bottom, right};
- return CameraPosition::New(env, map->cameraForGeometry(geometry, padding, bearing, tilt));
+ return CameraPosition::New(env, map->cameraForGeometry(geometry, padding, bearing, tilt), pixelRatio);
}
void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) {
@@ -514,23 +532,6 @@ void NativeMapView::setVisibleCoordinateBounds(JNIEnv& env, const jni::Array<jni
map->easeTo(cameraOptions, animationOptions);
}
-void NativeMapView::setContentPadding(JNIEnv&, float top, float left, float bottom, float right) {
- insets = {top, left, bottom, right};
- // invalidate the camera position to consider the new padding
- map->jumpTo(map->getCameraOptions(insets));
-}
-
-jni::Local<jni::Array<jni::jfloat>> NativeMapView::getContentPadding(JNIEnv& env) {
- auto result = jni::Array<jni::jfloat>::New(env, 4);
- std::vector<jfloat> vect;
- vect.push_back(insets.top());
- vect.push_back(insets.left());
- vect.push_back(insets.bottom());
- vect.push_back(insets.right());
- result.SetRegion<std::vector<jni::jfloat>>(env, 0, vect);
- return result;
-}
-
void NativeMapView::scheduleSnapshot(jni::JNIEnv&) {
mapRenderer.requestSnapshot([&](PremultipliedImage image) {
auto _env = android::AttachEnv();
@@ -548,7 +549,7 @@ void NativeMapView::scheduleSnapshot(jni::JNIEnv&) {
}
jni::Local<jni::Object<CameraPosition>> NativeMapView::getCameraPosition(jni::JNIEnv& env) {
- return CameraPosition::New(env, map->getCameraOptions(insets));
+ return CameraPosition::New(env, map->getCameraOptions(mbgl::nullopt), pixelRatio);
}
void NativeMapView::updateMarker(jni::JNIEnv& env, jni::jlong markerId, jni::jdouble lat, jni::jdouble lon, const jni::String& jid) {
@@ -1098,8 +1099,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::getBearing, "nativeGetBearing"),
METHOD(&NativeMapView::resetNorth, "nativeResetNorth"),
METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"),
- METHOD(&NativeMapView::setContentPadding, "nativeSetContentPadding"),
- METHOD(&NativeMapView::getContentPadding, "nativeGetContentPadding"),
METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"),
METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"),
METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"),
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index 4ea4781b36..ba2178022e 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -62,7 +62,7 @@ public:
void onDidFinishLoadingMap() override;
void onDidFailLoadingMap(MapLoadError, const std::string&) override;
void onWillStartRenderingFrame() override;
- void onDidFinishRenderingFrame(MapObserver::RenderMode) override;
+ void onDidFinishRenderingFrame(MapObserver::RenderFrameStatus) override;
void onWillStartRenderingMap() override;
void onDidFinishRenderingMap(MapObserver::RenderMode) override;
void onDidBecomeIdle() override;
@@ -91,15 +91,15 @@ public:
void moveBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong);
- void jumpTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble);
+ void jumpTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, const jni::Array<jni::jdouble>&);
- void easeTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, jni::jboolean);
+ void easeTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, const jni::Array<jni::jdouble>&, jni::jboolean);
- void flyTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble);
+ void flyTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, const jni::Array<jni::jdouble>&);
jni::Local<jni::Object<LatLng>> getLatLng(JNIEnv&);
- void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong);
+ void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, const jni::Array<jni::jdouble>&, jni::jlong);
jni::Local<jni::Object<CameraPosition>> getCameraForLatLngBounds(jni::JNIEnv&, const jni::Object<mbgl::android::LatLngBounds>&, double top, double left, double bottom, double right, double bearing, double tilt);
@@ -139,10 +139,6 @@ public:
void setVisibleCoordinateBounds(JNIEnv&, const jni::Array<jni::Object<LatLng>>&, const jni::Object<RectF>&, jni::jdouble, jni::jlong);
- void setContentPadding(JNIEnv&, float, float, float, float);
-
- jni::Local<jni::Array<jni::jfloat>> getContentPadding(JNIEnv&);
-
void scheduleSnapshot(jni::JNIEnv&);
jni::Local<jni::Object<CameraPosition>> getCameraPosition(jni::JNIEnv&);
@@ -259,7 +255,6 @@ private:
// Ensure these are initialised last
std::unique_ptr<mbgl::Map> map;
- mbgl::EdgeInsets insets;
};
} // namespace android
diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp
index 2966ecdfb0..a9cbfb57aa 100644
--- a/platform/android/src/run_loop.cpp
+++ b/platform/android/src/run_loop.cpp
@@ -31,8 +31,10 @@ int looperCallbackNew(int fd, int, void* data) {
int buffer[1];
while (read(fd, buffer, sizeof(buffer)) > 0) {}
- auto loop = reinterpret_cast<ALooper*>(data);
- ALooper_wake(loop);
+ auto runLoopImpl = reinterpret_cast<RunLoop::Impl*>(data);
+
+ runLoopImpl->coalesce.clear();
+ ALooper_wake(runLoopImpl->loop);
return 1;
}
@@ -42,9 +44,9 @@ int looperCallbackDefault(int fd, int, void* data) {
while (read(fd, buffer, sizeof(buffer)) > 0) {}
auto runLoopImpl = reinterpret_cast<RunLoop::Impl*>(data);
- auto runLoop = runLoopImpl->runLoop;
- runLoop->runOnce();
+ runLoopImpl->coalesce.clear();
+ runLoopImpl->runLoop->runOnce();
if (!runLoopImpl->running) {
ALooper_wake(runLoopImpl->loop);
@@ -99,7 +101,7 @@ RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) {
switch (type) {
case Type::New:
ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
- ALOOPER_EVENT_INPUT, looperCallbackNew, loop);
+ ALOOPER_EVENT_INPUT, looperCallbackNew, this);
break;
case Type::Default:
ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
@@ -129,6 +131,10 @@ RunLoop::Impl::~Impl() {
}
void RunLoop::Impl::wake() {
+ if (coalesce.test_and_set(std::memory_order_acquire)) {
+ return;
+ }
+
if (write(fds[PIPE_IN], "\n", 1) == -1) {
throw std::runtime_error("Failed to write to file descriptor.");
}
diff --git a/platform/android/src/run_loop_impl.hpp b/platform/android/src/run_loop_impl.hpp
index a76d636188..c6a1b23a7b 100644
--- a/platform/android/src/run_loop_impl.hpp
+++ b/platform/android/src/run_loop_impl.hpp
@@ -41,6 +41,7 @@ public:
ALooper* loop = nullptr;
RunLoop* runLoop = nullptr;
std::atomic<bool> running;
+ std::atomic_flag coalesce = ATOMIC_FLAG_INIT;
private:
friend RunLoop;
diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp
index 2eca6595e1..0b38269ada 100644
--- a/platform/android/src/snapshotter/map_snapshotter.cpp
+++ b/platform/android/src/snapshotter/map_snapshotter.cpp
@@ -23,7 +23,6 @@ MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env,
const jni::Object<LatLngBounds>& region,
const jni::Object<CameraPosition>& position,
jni::jboolean _showLogo,
- const jni::String& _programCacheDir,
const jni::String& _localIdeographFontFamily)
: javaPeer(_env, _obj)
, pixelRatio(_pixelRatio) {
@@ -39,7 +38,7 @@ MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env,
optional<mbgl::CameraOptions> cameraOptions;
if (position) {
- cameraOptions = CameraPosition::getCameraOptions(_env, position);
+ cameraOptions = CameraPosition::getCameraOptions(_env, position, pixelRatio);
}
optional<mbgl::LatLngBounds> bounds;
@@ -61,7 +60,6 @@ MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env,
pixelRatio,
cameraOptions,
bounds,
- jni::Make<std::string>(_env, _programCacheDir),
_localIdeographFontFamily ?
jni::Make<std::string>(_env, _localIdeographFontFamily) :
optional<std::string>{},
@@ -126,7 +124,7 @@ void MapSnapshotter::setSize(JNIEnv&, jni::jint width, jni::jint height) {
}
void MapSnapshotter::setCameraPosition(JNIEnv& env, const jni::Object<CameraPosition>& position) {
- auto options = CameraPosition::getCameraOptions(env, position);
+ auto options = CameraPosition::getCameraOptions(env, position, pixelRatio);
snapshotter->setCameraOptions(options);
}
@@ -161,7 +159,7 @@ void MapSnapshotter::registerNative(jni::JNIEnv& env) {
// Register the peer
jni::RegisterNativePeer<MapSnapshotter>(env, javaClass, "nativePtr",
- jni::MakePeer<MapSnapshotter, const jni::Object<MapSnapshotter>&, const jni::Object<FileSource>&, jni::jfloat, jni::jint, jni::jint, const jni::String&, const jni::String&, const jni::Object<LatLngBounds>&, const jni::Object<CameraPosition>&, jni::jboolean, const jni::String&, const jni::String&>,
+ jni::MakePeer<MapSnapshotter, const jni::Object<MapSnapshotter>&, const jni::Object<FileSource>&, jni::jfloat, jni::jint, jni::jint, const jni::String&, const jni::String&, const jni::Object<LatLngBounds>&, const jni::Object<CameraPosition>&, jni::jboolean, const jni::String&>,
"nativeInitialize",
"finalize",
METHOD(&MapSnapshotter::setStyleUrl, "setStyleUrl"),
diff --git a/platform/android/src/snapshotter/map_snapshotter.hpp b/platform/android/src/snapshotter/map_snapshotter.hpp
index 791aa61d6a..608a4c855f 100644
--- a/platform/android/src/snapshotter/map_snapshotter.hpp
+++ b/platform/android/src/snapshotter/map_snapshotter.hpp
@@ -34,7 +34,6 @@ public:
const jni::Object<LatLngBounds>& region,
const jni::Object<CameraPosition>& position,
jni::jboolean showLogo,
- const jni::String& programCacheDir,
const jni::String& localIdeographFontFamily);
~MapSnapshotter();
diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp
index 8559720b2f..dc0a0acb31 100644
--- a/platform/android/src/style/android_conversion.hpp
+++ b/platform/android/src/style/android_conversion.hpp
@@ -47,9 +47,18 @@ public:
}
template <class Fn>
- static optional<Error> eachMember(const mbgl::android::Value&, Fn&&) {
- // TODO
- mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
+ static optional<Error> eachMember(const mbgl::android::Value& value, Fn&& fn) {
+ assert(value.isObject());
+ mbgl::android::Value keys = value.keyArray();
+ std::size_t length = arrayLength(keys);
+ for(std::size_t i = 0; i < length; ++i){
+ const auto k = keys.get(i).toString();
+ auto v = value.get(k.c_str());
+ optional<Error> result = fn(k, std::move(v));
+ if (result) {
+ return result;
+ }
+ }
return {};
}
@@ -94,8 +103,7 @@ public:
} else if (value.isString()) {
return { value.toString() };
} else if (value.isNumber()) {
- auto doubleVal = value.toDouble();
- return { doubleVal - (int) doubleVal > 0.0 ? doubleVal : value.toLong() };
+ return { value.toDouble() };
} else {
return {};
}
diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp
index fb06255913..53b35a9a13 100644
--- a/platform/android/src/style/layers/symbol_layer.cpp
+++ b/platform/android/src/style/layers/symbol_layer.cpp
@@ -201,6 +201,11 @@ namespace android {
return std::move(*convert<jni::Local<jni::Object<>>>(env, toSymbolLayer(layer).getTextMaxAngle()));
}
+ jni::Local<jni::Object<>> SymbolLayer::getTextWritingMode(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ return std::move(*convert<jni::Local<jni::Object<>>>(env, toSymbolLayer(layer).getTextWritingMode()));
+ }
+
jni::Local<jni::Object<>> SymbolLayer::getTextRotate(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
return std::move(*convert<jni::Local<jni::Object<>>>(env, toSymbolLayer(layer).getTextRotate()));
@@ -534,6 +539,7 @@ namespace android {
METHOD(&SymbolLayer::getTextVariableAnchor, "nativeGetTextVariableAnchor"),
METHOD(&SymbolLayer::getTextAnchor, "nativeGetTextAnchor"),
METHOD(&SymbolLayer::getTextMaxAngle, "nativeGetTextMaxAngle"),
+ METHOD(&SymbolLayer::getTextWritingMode, "nativeGetTextWritingMode"),
METHOD(&SymbolLayer::getTextRotate, "nativeGetTextRotate"),
METHOD(&SymbolLayer::getTextPadding, "nativeGetTextPadding"),
METHOD(&SymbolLayer::getTextKeepUpright, "nativeGetTextKeepUpright"),
diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp
index d2ced89191..9e494e678a 100644
--- a/platform/android/src/style/layers/symbol_layer.hpp
+++ b/platform/android/src/style/layers/symbol_layer.hpp
@@ -90,6 +90,8 @@ public:
jni::Local<jni::Object<jni::ObjectTag>> getTextMaxAngle(jni::JNIEnv&);
+ jni::Local<jni::Object<jni::ObjectTag>> getTextWritingMode(jni::JNIEnv&);
+
jni::Local<jni::Object<jni::ObjectTag>> getTextRotate(jni::JNIEnv&);
jni::Local<jni::Object<jni::ObjectTag>> getTextPadding(jni::JNIEnv&);
diff --git a/platform/android/src/style/sources/custom_geometry_source.cpp b/platform/android/src/style/sources/custom_geometry_source.cpp
index 057f5c99ba..dccca4cf46 100644
--- a/platform/android/src/style/sources/custom_geometry_source.cpp
+++ b/platform/android/src/style/sources/custom_geometry_source.cpp
@@ -65,7 +65,12 @@ namespace android {
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(*_env);
static auto fetchTile = javaClass.GetMethod<void (jni::jint, jni::jint, jni::jint)>(*_env, "fetchTile");
- assert(javaPeer);
+ // The source is removed on the main thread, but it still exists on the Render thread until the frame is complete.
+ // This might cause fetchTile/cancelTile invocations when the Java thread is shutting down and the peer has already been released.
+ // See https://github.com/mapbox/mapbox-gl-native/issues/12551.
+ if(!javaPeer) {
+ return;
+ }
auto peer = jni::Cast(*_env, javaClass, javaPeer);
peer.Call(*_env, fetchTile, (int)tileID.z, (int)tileID.x, (int)tileID.y);
@@ -77,7 +82,12 @@ namespace android {
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(*_env);
static auto cancelTile = javaClass.GetMethod<void (jni::jint, jni::jint, jni::jint)>(*_env, "cancelTile");
- assert(javaPeer);
+ // The source is removed on the main thread, but it still exists on the Render thread until the frame is complete.
+ // This might cause fetchTile/cancelTile invocations when the Java thread is shutting down and the peer has already been released.
+ // See https://github.com/mapbox/mapbox-gl-native/issues/12551.
+ if(!javaPeer) {
+ return;
+ }
auto peer = jni::Cast(*_env, javaClass, javaPeer);
peer.Call(*_env, cancelTile, (int)tileID.z, (int)tileID.x, (int)tileID.y);
diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp
index be4c0367fc..786e5cb586 100644
--- a/platform/android/src/style/sources/source.cpp
+++ b/platform/android/src/style/sources/source.cpp
@@ -133,7 +133,7 @@ namespace android {
// Release the peer relationships. These will be re-established when the source is added to a map
assert(ownedSource->peer.has_value());
ownedSource->peer.get<std::unique_ptr<Source>>().release();
- ownedSource->peer = util::peer();
+ ownedSource->peer = mapbox::base::TypeWrapper();
// Release the strong reference to the java peer
assert(javaPeer);
diff --git a/platform/android/src/style/value.cpp b/platform/android/src/style/value.cpp
index f916909687..2f04840729 100644
--- a/platform/android/src/style/value.cpp
+++ b/platform/android/src/style/value.cpp
@@ -63,6 +63,12 @@ namespace android {
return Value(env, jni::Local<jni::Object<>>(env, member));
}
+ Value Value::keyArray() const{
+ jni::jobject* set = jni::CallMethod<jni::jobject*>(env, value.get(), *java::Map::keySetMethodId);
+ jni::jobject* array = jni::CallMethod<jni::jobject*>(env, set, *java::Set::toArrayMethodId);
+ return Value(env, jni::Local<jni::Object<>>(env, array));
+ }
+
int Value::getLength() const {
auto array = (jni::jarray<jni::jobject>*) value.get();
return jni::GetArrayLength(env, *array);
diff --git a/platform/android/src/style/value.hpp b/platform/android/src/style/value.hpp
index 0c702bb465..b507c5ed11 100644
--- a/platform/android/src/style/value.hpp
+++ b/platform/android/src/style/value.hpp
@@ -31,6 +31,7 @@ public:
long toLong() const;
bool toBool() const;
Value get(const char* key) const;
+ Value keyArray() const;
int getLength() const;
Value get(const int index ) const;
diff --git a/platform/android/src/test/runtime.cpp b/platform/android/src/test/runtime.cpp
index 9cf79c501c..1f8f16da3c 100644
--- a/platform/android/src/test/runtime.cpp
+++ b/platform/android/src/test/runtime.cpp
@@ -1,5 +1,6 @@
#include "runtime.hpp"
#include "../jni.hpp"
+#include "../jni_native.hpp"
#include <cassert>
#include <dlfcn.h>
diff --git a/platform/android/vendor/mapbox-gestures-android b/platform/android/vendor/mapbox-gestures-android
-Subproject 22910190e8374e1ad9494e9c98ffb027eafdd22
+Subproject ae08de82ca178dca7f005dfc8056554cf77124a
diff --git a/platform/android/vendor/mapbox-java b/platform/android/vendor/mapbox-java
-Subproject 305f6a78d1c1c4ec45735b0cf98bd7c4ea4658f
+Subproject 84de2d4b02e93a9eda9d0254332cd356d17445d
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 37454cba54..38066c9f43 100755
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -5,6 +5,7 @@ const fs = require('fs');
const ejs = require('ejs');
const _ = require('lodash');
const colorParser = require('csscolorparser');
+const assert = require('assert');
require('../../../scripts/style-code');
@@ -19,34 +20,56 @@ delete spec.layout_circle["circle-sort-key"]
delete spec.layout_line["line-sort-key"]
delete spec.layout_fill["fill-sort-key"]
+class ConventionOverride {
+ constructor(val) {
+ if (typeof val === 'string') {
+ this.name_ = val;
+ this.enumName_ = null;
+ } else if (val instanceof Object) {
+ this.name_ = val.name;
+ this.enumName_ = val.enumName;
+ } else {
+ assert(false);
+ }
+ }
+
+ set name(name_) { this.name_ = name_; }
+ get name() { return this.name_; }
+ get enumName() { return this.enumName_ || this.name_; }
+}
+
// Rename properties and keep `original` for use with setters and getters
_.forOwn(cocoaConventions, function (properties, kind) {
- _.forOwn(properties, function (newName, oldName) {
+ _.forOwn(properties, function (newConvention, oldName) {
+ let conventionOverride = new ConventionOverride(newConvention);
let property = spec[kind][oldName];
- if (newName.startsWith('is-')) {
- property.getter = newName;
- newName = newName.substr(3);
+ if (conventionOverride.name.startsWith('is-')) {
+ property.getter = conventionOverride.name;
+ conventionOverride.name = conventionOverride.name.substr(3);
}
- if (newName !== oldName) {
+
+ // Override enum name based on style-spec-cocoa-conventions-v8.json
+ property.enumName = conventionOverride.enumName;
+
+ if (conventionOverride.name !== oldName) {
property.original = oldName;
+ delete spec[kind][oldName];
+ spec[kind][conventionOverride.name] = property;
}
- delete spec[kind][oldName];
- spec[kind][newName] = property;
// Update cross-references to this property in other properties'
// documentation and requirements.
let renameCrossReferences = function (property, name) {
- property.doc = property.doc.replace(new RegExp('`' + oldName + '`', 'g'), '`' + newName + '`');
+ property.doc = property.doc.replace(new RegExp('`' + oldName + '`', 'g'), '`' + conventionOverride.name + '`');
let requires = property.requires || [];
for (let i = 0; i < requires.length; i++) {
if (requires[i] === oldName) {
- property.requires[i] = newName;
+ property.requires[i] = conventionOverride.name;
}
if (typeof requires[i] !== 'string') {
- let prop = name;
_.forOwn(requires[i], function (values, name, require) {
if (name === oldName) {
- require[newName] = values;
+ require[conventionOverride.name] = values;
delete require[name];
}
});
@@ -79,6 +102,28 @@ global.camelizeWithLeadingLowercase = function (str) {
});
};
+// Returns true only if property is an enum or if it is an array
+// property with uniquely defined enum.
+global.definesEnum = function(property, allProperties) {
+ if (property.type === "enum") {
+ return true;
+ }
+
+ if (property.type === 'array' && property.value === 'enum') {
+ const uniqueArrayEnum = (prop, enums) => {
+ if (prop.value !== 'enum') return false;
+ const enumsEqual = (val1, val2) => val1.length === val1.length && val1.every((val, i) => val === val2[i]);
+ return enums.filter(e => enumsEqual(Object.keys(prop.values).sort(), Object.keys(e.values).sort())).length == 0;
+ };
+
+ const allEnumProperties = _(allProperties).filter({'type': 'enum'}).value();
+ const uniqueArrayEnumProperties = _(allProperties).filter({'type': 'array'}).filter(prop => uniqueArrayEnum(prop, allEnumProperties)).value();
+ return _(uniqueArrayEnumProperties).filter({'name': property.name}).value().length != 0;
+ }
+
+ return false;
+}
+
global.objCName = function (property) {
return camelizeWithLeadingLowercase(property.name);
};
@@ -144,6 +189,8 @@ global.objCTestValue = function (property, layerType, arraysAsStructs, indent) {
}
case 'anchor':
return `@"{'top','bottom'}"`;
+ case 'mode':
+ return `@"{'horizontal','vertical'}"`;
default:
throw new Error(`unknown array type for ${property.name}`);
}
@@ -194,6 +241,8 @@ global.mbglTestValue = function (property, layerType) {
return '{ 1, 1 }';
case 'anchor':
return '{ mbgl::style::SymbolAnchorType::Top, mbgl::style::SymbolAnchorType::Bottom }';
+ case 'mode':
+ return '{ mbgl::style::TextWritingModeType::Horizontal, mbgl::style::TextWritingModeType::Vertical }';
default:
throw new Error(`unknown array type for ${property.name}`);
}
@@ -213,6 +262,8 @@ global.mbglExpressionTestValue = function (property, layerType) {
switch (arrayType(property)) {
case 'anchor':
return `{"top", "bottom"}`;
+ case 'mode':
+ return `{"horizontal", "vertical"}`;
default:
break;
}
@@ -358,7 +409,7 @@ global.propertyDoc = function (propertyName, property, layerType, kind) {
doc += '* Any of the following constant string values:\n';
doc += Object.keys(property.values).map(value => ' * `' + value + '`: ' + property.values[value].doc).join('\n') + '\n';
} else if (property.type === 'array' && property.value === 'enum') {
- doc += '* Constant array, whose each element is any of the following constant string values:\n';
+ doc += '* Constant array, in which each element is any of the following constant string values:\n';
doc += Object.keys(property.values).map(value => ' * `' + value + '`: ' + property.values[value].doc).join('\n') + '\n';
}
if (property.type === 'formatted') {
@@ -439,6 +490,8 @@ global.describeType = function (property) {
return '`MGLSphericalPosition`';
case 'anchor':
return '`MGLTextAnchor` array';
+ case 'mode':
+ return '`MGLTextWritingMode` array';
default:
return 'array';
}
@@ -543,6 +596,10 @@ global.originalPropertyName = function (property) {
return property.original || property.name;
};
+global.enumName = function (property) {
+ return property.enumName || property.name;
+};
+
global.propertyType = function (property) {
switch (property.type) {
case 'boolean':
@@ -568,6 +625,7 @@ global.propertyType = function (property) {
case 'translate':
return 'NSValue *';
case 'anchor':
+ case 'mode':
return 'NSArray<NSValue *> *';
default:
throw new Error(`unknown array type for ${property.name}`);
@@ -615,6 +673,8 @@ global.valueTransformerArguments = function (property) {
return ['std::array<float, 2>', objCType];
case 'anchor':
return ['std::vector<mbgl::style::SymbolAnchorType>', objCType, 'mbgl::style::SymbolAnchorType', 'MGLTextAnchor'];
+ case 'mode':
+ return ['std::vector<mbgl::style::TextWritingModeType>', objCType, 'mbgl::style::TextWritingModeType', 'MGLTextWritingMode'];
default:
throw new Error(`unknown array type for ${property.name}`);
}
@@ -666,6 +726,8 @@ global.mbglType = function(property) {
return 'mbgl::style::Position';
case 'anchor':
return 'std::vector<mbgl::style::SymbolAnchorType>';
+ case 'mode':
+ return 'std::vector<mbgl::style::TextWritingModeType>';
default:
throw new Error(`unknown array type for ${property.name}`);
}
@@ -773,7 +835,7 @@ var renamedPropertiesByLayerType = {};
for (var layer of layers) {
layer.properties = _.concat(layer.layoutProperties, layer.paintProperties);
- let enumProperties = _.filter(layer.properties, prop => prop.type === 'enum');
+ let enumProperties = _.filter(layer.properties, prop => definesEnum(prop, layer.properties));
if (enumProperties.length) {
layer.enumProperties = enumProperties;
}
diff --git a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
index ce313ddbb9..c781879bc5 100644
--- a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
+++ b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
@@ -18,7 +18,8 @@
"text-max-width": "maximum-text-width",
"text-optional": "is-text-optional",
"text-rotate": "text-rotation",
- "text-size": "text-font-size"
+ "text-size": "text-font-size",
+ "text-writing-mode": {"name": "text-writing-modes", "enumName": "text-writing-mode"}
},
"paint_circle": {
"circle-pitch-scale": "circle-scale-alignment",
diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m
index 69170459bc..c37195967a 100644
--- a/platform/darwin/src/MGLAccountManager.m
+++ b/platform/darwin/src/MGLAccountManager.m
@@ -13,6 +13,8 @@ static NSString * const MGLAccountManagerExternalClassName = @"MBXAccounts";
static NSString * const MGLAccountManagerExternalMethodName = @"skuToken";
#endif
+NSString * const MGLMapboxAccountTypeKey = @"MGLMapboxAccountType";
+
@interface MGLAccountManager ()
@property (atomic) NSString *accessToken;
diff --git a/platform/darwin/src/MGLAccountManager_Private.h b/platform/darwin/src/MGLAccountManager_Private.h
index 3fd45f1ae8..4bf7963182 100644
--- a/platform/darwin/src/MGLAccountManager_Private.h
+++ b/platform/darwin/src/MGLAccountManager_Private.h
@@ -2,6 +2,9 @@
NS_ASSUME_NONNULL_BEGIN
+/// NSUserDefaults key that controls developer account type
+FOUNDATION_EXTERN NSString * const MGLMapboxAccountTypeKey;
+
@interface MGLAccountManager (Private)
/// Returns the shared instance of the `MGLAccountManager` class.
diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm
index 5110435b03..a04181af2f 100644
--- a/platform/darwin/src/MGLComputedShapeSource.mm
+++ b/platform/darwin/src/MGLComputedShapeSource.mm
@@ -199,6 +199,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
}
- (void)setFeatures:(NSArray<MGLShape <MGLFeature> *>*)features inTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel {
+ MGLAssertStyleSourceIsValid();
mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID((uint8_t)zoomLevel, (uint32_t)x, (uint32_t)y);
mbgl::FeatureCollection featureCollection;
featureCollection.reserve(features.count);
@@ -236,10 +237,12 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi
}
- (void) invalidateBounds:(MGLCoordinateBounds)bounds {
+ MGLAssertStyleSourceIsValid();
((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateRegion(MGLLatLngBoundsFromCoordinateBounds(bounds));
}
- (void) invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)z {
+ MGLAssertStyleSourceIsValid();
((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateTile(mbgl::CanonicalTileID(z, (unsigned int)x, (unsigned int)y));
}
diff --git a/platform/darwin/src/MGLForegroundStyleLayer.mm b/platform/darwin/src/MGLForegroundStyleLayer.mm
index 76700d6f77..eaa5e83e59 100644
--- a/platform/darwin/src/MGLForegroundStyleLayer.mm
+++ b/platform/darwin/src/MGLForegroundStyleLayer.mm
@@ -1,4 +1,5 @@
#import "MGLForegroundStyleLayer.h"
+#import "MGLStyleLayer_Private.h"
@implementation MGLForegroundStyleLayer
@@ -9,10 +10,17 @@
}
- (NSString *)description {
- return [NSString stringWithFormat:
- @"<%@: %p; identifier = %@; sourceIdentifier = %@; visible = %@>",
- NSStringFromClass([self class]), (void *)self, self.identifier,
- self.sourceIdentifier, self.visible ? @"YES" : @"NO"];
+ if (self.rawLayer) {
+ return [NSString stringWithFormat:
+ @"<%@: %p; identifier = %@; sourceIdentifier = %@; visible = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier,
+ self.sourceIdentifier, self.visible ? @"YES" : @"NO"];
+ }
+ else {
+ return [NSString stringWithFormat:
+ @"<%@: %p; identifier = %@; sourceIdentifier = <unknown>; visible = NO>",
+ NSStringFromClass([self class]), (void *)self, self.identifier];
+ }
}
@end
diff --git a/platform/darwin/src/MGLImageSource.mm b/platform/darwin/src/MGLImageSource.mm
index 351247e901..421cc7a155 100644
--- a/platform/darwin/src/MGLImageSource.mm
+++ b/platform/darwin/src/MGLImageSource.mm
@@ -45,11 +45,13 @@
}
- (NSURL *)URL {
+ MGLAssertStyleSourceIsValid();
auto url = self.rawSource->getURL();
return url ? [NSURL URLWithString:@(url->c_str())] : nil;
}
- (void)setURL:(NSURL *)url {
+ MGLAssertStyleSourceIsValid();
if (url) {
self.rawSource->setURL(url.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
_image = nil;
@@ -59,6 +61,7 @@
}
- (void)setImage:(MGLImage *)image {
+ MGLAssertStyleSourceIsValid();
if (image != nullptr) {
self.rawSource->setImage(image.mgl_premultipliedImage);
} else {
@@ -68,16 +71,27 @@
}
- (MGLCoordinateQuad)coordinates {
+ MGLAssertStyleSourceIsValid();
return MGLCoordinateQuadFromLatLngArray(self.rawSource->getCoordinates());
}
- (void)setCoordinates: (MGLCoordinateQuad)coordinateQuad {
+ MGLAssertStyleSourceIsValid();
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];
+ if (self.rawSource) {
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@; coordinates = %@; URL = %@; image = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier,
+ MGLStringFromCoordinateQuad(self.coordinates),
+ self.URL,
+ self.image];
+ }
+ else {
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@; coordinates = <unknown>; URL = <unknown>; image = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier, self.image];
+ }
}
- (mbgl::style::ImageSource *)rawSource {
@@ -85,6 +99,7 @@
}
- (NSString *)attributionHTMLString {
+ MGLAssertStyleSourceIsValid();
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
}
diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h
index 1ee9bd99bb..33febe0d0c 100644
--- a/platform/darwin/src/MGLMapSnapshotter.h
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -6,6 +6,30 @@
NS_ASSUME_NONNULL_BEGIN
/**
+ An overlay that is placed within a `MGLMapSnapshot`.
+ To access this object, use `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]`.
+ */
+
+MGL_EXPORT
+@interface MGLMapSnapshotOverlay : NSObject
+
+/**
+ The current `CGContext` that snapshot is drawing within. You may use this context
+ to perform additional custom drawing.
+ */
+@property (nonatomic, readonly) CGContextRef context;
+
+@end
+
+/**
+A block provided during the snapshot drawing process, enabling the ability to
+draw custom overlays rendered with Core Graphics.
+
+ @param snapshotOverlay The `MGLMapSnapshotOverlay` provided during snapshot drawing.
+ */
+typedef void (^MGLMapSnapshotOverlayHandler)(MGLMapSnapshotOverlay * snapshotOverlay);
+
+/**
The options to use when creating images with the `MGLMapSnapshotter`.
*/
MGL_EXPORT
@@ -201,6 +225,15 @@ MGL_EXPORT
- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
/**
+ Starts the snapshot creation and executes the specified blocks with the result
+ on the specified queue. Use this option if you want to add custom drawing on top of the
+ resulting `MGLMapSnapShot`.
+ @param overlayHandler The block to handle manipulation of the `MGLMapSnapshotter`'s `CGContext`.
+ @param completionHandler The block to handle the result in.
+ */
+- (void)startWithOverlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
+
+/**
Cancels the snapshot creation request, if any.
Once you call this method, you cannot resume the snapshot. In order to obtain
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
index 65bed2cf42..3a258d146a 100644
--- a/platform/darwin/src/MGLMapSnapshotter.mm
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -32,6 +32,26 @@
const CGPoint MGLLogoImagePosition = CGPointMake(8, 8);
const CGFloat MGLSnapshotterMinimumPixelSize = 64;
+
+@interface MGLMapSnapshotOverlay()
+
+- (instancetype)initWithContext:(CGContextRef)context;
+
+@end
+
+@implementation MGLMapSnapshotOverlay
+
+- (instancetype) initWithContext:(CGContextRef)context {
+ self = [super init];
+ if (self) {
+ _context = context;
+ }
+
+ return self;
+}
+
+@end
+
@implementation MGLMapSnapshotOptions
- (instancetype _Nonnull)initWithStyleURL:(nullable NSURL *)styleURL camera:(MGLMapCamera *)camera size:(CGSize)size
@@ -183,7 +203,15 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
[self startWithQueue:dispatch_get_main_queue() completionHandler:completion];
}
-- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completion
+- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler {
+ [self startWithQueue:queue overlayHandler:nil completionHandler:completionHandler];
+}
+
+- (void)startWithOverlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler completionHandler:(MGLMapSnapshotCompletionHandler)completion {
+ [self startWithQueue:dispatch_get_main_queue() overlayHandler:overlayHandler completionHandler:completion];
+}
+
+- (void)startWithQueue:(dispatch_queue_t)queue overlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler completionHandler:(MGLMapSnapshotCompletionHandler)completion
{
if (!mbgl::Scheduler::GetCurrent()) {
[NSException raise:NSInvalidArgumentException
@@ -210,8 +238,8 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
// capture weakSelf to avoid retain cycle if callback is never called (ie snapshot cancelled)
_snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(
- *mbgl::Scheduler::GetCurrent(),
- [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn) {
+ *mbgl::Scheduler::GetCurrent(),
+ [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn, mbgl::MapSnapshotter::LatLngForFn latLngForFn) {
__typeof__(self) strongSelf = weakSelf;
// If self had died, _snapshotCallback would have been destroyed and this block would not be executed
@@ -224,7 +252,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
NSString *description = @(mbgl::util::toString(mbglError).c_str());
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeSnapshotFailed userInfo:userInfo];
-
+
// Dispatch to result queue
dispatch_async(queue, ^{
strongSelf.completion(nil, error);
@@ -238,11 +266,12 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
mglImage.size = NSMakeSize(mglImage.size.width / strongSelf.options.scale,
mglImage.size.height / strongSelf.options.scale);
#endif
- [strongSelf drawAttributedSnapshot:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn];
+
+ [strongSelf drawAttributedSnapshot:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn overlayHandler:overlayHandler];
}
strongSelf->_snapshotCallback = NULL;
- });
+ });
// Launches snapshot on background Thread owned by mbglMapSnapshotter
// _snapshotCallback->self() is an ActorRef: if the callback is destroyed, further messages
@@ -250,7 +279,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
_mbglMapSnapshotter->snapshot(_snapshotCallback->self());
}
-+ (MGLImage*)drawAttributedSnapshotWorker:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn scale:(CGFloat)scale size:(CGSize)size {
++ (MGLImage*)drawAttributedSnapshotWorker:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn scale:(CGFloat)scale size:(CGSize)size overlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler {
NSArray<MGLAttributionInfo *>* attributionInfo = [MGLMapSnapshotter generateAttributionInfos:attributions];
@@ -292,7 +321,23 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, scale);
[mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)];
-
+
+ CGContextRef currentContext = UIGraphicsGetCurrentContext();
+
+ if (currentContext && overlayHandler) {
+ MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext];
+ CGContextSaveGState(snapshotOverlay.context);
+ overlayHandler(snapshotOverlay);
+ CGContextRestoreGState(snapshotOverlay.context);
+ currentContext = UIGraphicsGetCurrentContext();
+ }
+
+ if (!currentContext && overlayHandler) {
+ // If the current context has been corrupted by the user,
+ // return nil so we can generate an error later.
+ return nil;
+ }
+
[logoImage drawInRect:logoImageRect];
UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext();
@@ -359,6 +404,21 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
[sourceImageRep drawInRect: targetFrame];
+ NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+ if (currentContext && overlayHandler) {
+ MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext.CGContext];
+ [currentContext saveGraphicsState];
+ overlayHandler(snapshotOverlay);
+ [currentContext restoreGraphicsState];
+ currentContext = [NSGraphicsContext currentContext];
+ }
+
+ if (!currentContext && overlayHandler) {
+ // If the current context has been corrupted by the user,
+ // return nil so we can generate an error later.
+ return nil;
+ }
+
if (logoImage) {
[logoImage drawInRect:logoImageRect];
}
@@ -379,8 +439,8 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
#endif
}
-- (void)drawAttributedSnapshot:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn {
-
+- (void)drawAttributedSnapshot:(mbgl::MapSnapshotter::Attributions)attributions snapshotImage:(MGLImage *)mglImage pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn overlayHandler:(MGLMapSnapshotOverlayHandler)overlayHandler {
+
// Process image watermark in a work queue
dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t resultQueue = self.resultQueue;
@@ -394,19 +454,30 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
dispatch_async(workQueue, ^{
// Call a class method to ensure we're not accidentally capturing self
- MGLImage *compositedImage = [MGLMapSnapshotter drawAttributedSnapshotWorker:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn scale:scale size:size];
+ MGLImage *compositedImage = [MGLMapSnapshotter drawAttributedSnapshotWorker:attributions snapshotImage:mglImage pointForFn:pointForFn latLngForFn:latLngForFn scale:scale size:size overlayHandler:overlayHandler];
// Dispatch result to origin queue
dispatch_async(resultQueue, ^{
__typeof__(self) strongself = weakself;
if (strongself.completion) {
- MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage
- scale:scale
- pointForFn:pointForFn
- latLngForFn:latLngForFn];
- strongself.completion(snapshot, nil);
- strongself.completion = nil;
+
+ if (!compositedImage) {
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"Failed to generate composited snapshot."};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain
+ code:MGLErrorCodeSnapshotFailed
+ userInfo:userInfo];
+
+ strongself.completion(nil, error);
+ strongself.completion = nil;
+ } else {
+ MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage
+ scale:scale
+ pointForFn:pointForFn
+ latLngForFn:latLngForFn];
+ strongself.completion(snapshot, nil);
+ strongself.completion = nil;
+ }
}
});
});
@@ -624,7 +695,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
// Create the snapshotter
_mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(
- style, size, pixelRatio, cameraOptions, coordinateBounds, config.cacheDir, config.localFontFamilyName, resourceOptions);
+ style, size, pixelRatio, cameraOptions, coordinateBounds, config.localFontFamilyName, resourceOptions);
}
@end
diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h
index 1ed364ddf3..d093bb938a 100644
--- a/platform/darwin/src/MGLOfflineStorage.h
+++ b/platform/darwin/src/MGLOfflineStorage.h
@@ -173,6 +173,11 @@ typedef NS_ENUM(NSUInteger, MGLResourceKind) {
packs and ambient caching. All of this class’s instance methods are asynchronous,
reflecting the fact that offline resources are stored in a database. The shared
object maintains a canonical collection of offline packs in its `packs` property.
+
+ Mapbox resources downloaded via this API are subject to separate Vector Tile and
+ Raster Tile API pricing and are not included in the Maps SDK’s “unlimited” requests.
+ See <a href="https://www.mapbox.com/pricing/">our pricing page</a> for more
+ information.
#### Related examples
See the <a href="https://docs.mapbox.com/ios/maps/examples/offline-pack/">
@@ -341,8 +346,7 @@ MGL_EXPORT
attempt to download additional tiles until already downloaded tiles are removed
by calling the `-removePack:withCompletionHandler:` method.
- @note The <a href="https://www.mapbox.com/tos/">Mapbox Terms of Service</a>
- prohibits changing or bypassing this limit without permission from Mapbox.
+ @param maximumCount The maximum number of tiles allowed to be downloaded.
*/
- (void)setMaximumAllowedMapboxTiles:(uint64_t)maximumCount;
@@ -420,7 +424,7 @@ MGL_EXPORT
- (void)resetDatabaseWithCompletionHandler:(void (^)(NSError *_Nullable error))completion;
-/*
+/**
Inserts the provided resource into the ambient cache.
This method mimics the caching that would take place if the equivalent resource
diff --git a/platform/darwin/src/MGLRasterTileSource.mm b/platform/darwin/src/MGLRasterTileSource.mm
index 61e9ef97fd..e89367711e 100644
--- a/platform/darwin/src/MGLRasterTileSource.mm
+++ b/platform/darwin/src/MGLRasterTileSource.mm
@@ -65,11 +65,13 @@ static const CGFloat MGLRasterTileSourceRetinaTileSize = 512;
}
- (NSURL *)configurationURL {
+ MGLAssertStyleSourceIsValid();
auto url = self.rawSource->getURL();
return url ? [NSURL URLWithString:@(url->c_str())] : nil;
}
- (NSString *)attributionHTMLString {
+ MGLAssertStyleSourceIsValid();
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
}
diff --git a/platform/darwin/src/MGLRasterTileSource_Private.h b/platform/darwin/src/MGLRasterTileSource_Private.h
index 128dcb447d..8502b811e2 100644
--- a/platform/darwin/src/MGLRasterTileSource_Private.h
+++ b/platform/darwin/src/MGLRasterTileSource_Private.h
@@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MGLRasterTileSource (Private)
-@property (nonatomic, readonly) mbgl::style::RasterSource *rawSource;
+@property (nonatomic, readonly, nullable) mbgl::style::RasterSource *rawSource;
- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize;
diff --git a/platform/darwin/src/MGLRendererConfiguration.h b/platform/darwin/src/MGLRendererConfiguration.h
index bfe88b7209..0c539f86f1 100644
--- a/platform/darwin/src/MGLRendererConfiguration.h
+++ b/platform/darwin/src/MGLRendererConfiguration.h
@@ -20,9 +20,6 @@ MGL_EXPORT
Based on the native scale where available, otherwise the standard screen scale. */
@property (nonatomic, readonly) const float scaleFactor;
-/** The cache dir to use. */
-@property (nonatomic, readonly) mbgl::optional<std::string> cacheDir;
-
/** The name of the font family to use for client-side text rendering of CJK ideographs.
Set MGLIdeographicFontFamilyName in your containing application's Info.plist to
diff --git a/platform/darwin/src/MGLRendererConfiguration.mm b/platform/darwin/src/MGLRendererConfiguration.mm
index 54bdcaa691..136dc929a6 100644
--- a/platform/darwin/src/MGLRendererConfiguration.mm
+++ b/platform/darwin/src/MGLRendererConfiguration.mm
@@ -66,10 +66,6 @@ static NSString * const MGLIdeographicFontFamilyNameKey = @"MGLIdeographicFontFa
#endif
}
-- (mbgl::optional<std::string>)cacheDir {
- return mbgl::optional<std::string>();
-}
-
- (mbgl::optional<std::string>)localFontFamilyName {
return [self _localFontFamilyNameWithPropertyDictionary:[[NSBundle mainBundle] infoDictionary]];
}
diff --git a/platform/darwin/src/MGLSDKMetricsManager.m b/platform/darwin/src/MGLSDKMetricsManager.m
index ed48eaf0c1..828fbcd505 100644
--- a/platform/darwin/src/MGLSDKMetricsManager.m
+++ b/platform/darwin/src/MGLSDKMetricsManager.m
@@ -42,12 +42,21 @@ NSString* MGLStringFromMetricType(MGLMetricType metricType) {
[UIScreen mainScreen].bounds.size.height];
NSLocale *currentLocale = [NSLocale currentLocale];
- NSString *country = [currentLocale objectForKey:NSLocaleCountryCode];
+
+ NSString *country = [currentLocale objectForKey:NSLocaleCountryCode] ?: @"unknown";
NSString *device = deviceName();
- const NXArchInfo localArchInfo = *NXGetLocalArchInfo();
- NSString *abi = [NSString stringWithUTF8String:localArchInfo.description];
+ NSString *abi = @"unknown";
+
+ {
+ const NXArchInfo *localArchInfo = NXGetLocalArchInfo();
+
+ if (localArchInfo) {
+ abi = @(localArchInfo->description);
+ NXFreeArchInfo(localArchInfo);
+ }
+ }
NSString *ram = [NSString stringWithFormat:@"%llu", [NSProcessInfo processInfo].physicalMemory];
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index fc526f9850..3628a0eb74 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -150,11 +150,13 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap
}
- (NSURL *)URL {
+ MGLAssertStyleSourceIsValid();
auto url = self.rawSource->getURL();
return url ? [NSURL URLWithString:@(url->c_str())] : nil;
}
- (void)setURL:(NSURL *)url {
+ MGLAssertStyleSourceIsValid();
if (url) {
self.rawSource->setURL(url.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
_shape = nil;
@@ -164,17 +166,24 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap
}
- (void)setShape:(MGLShape *)shape {
+ MGLAssertStyleSourceIsValid();
self.rawSource->setGeoJSON({ shape.geoJSONObject });
_shape = shape;
}
- (NSString *)description {
- return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = %@; shape = %@>",
- NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape];
+ if (self.rawSource) {
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = %@; shape = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape];
+ }
+ else {
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@; URL = <unknown>; shape = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier, self.shape];
+ }
}
- (NSArray<id <MGLFeature>> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate {
-
+ MGLAssertStyleSourceIsValid();
mbgl::optional<mbgl::style::Filter> optionalFilter;
if (predicate) {
optionalFilter = predicate.mgl_filter;
@@ -190,6 +199,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap
#pragma mark - MGLCluster management
- (mbgl::optional<mbgl::FeatureExtensionValue>)featureExtensionValueOfCluster:(MGLShape<MGLCluster> *)cluster extension:(std::string)extension options:(const std::map<std::string, mbgl::Value>)options {
+ MGLAssertStyleSourceIsValid();
mbgl::optional<mbgl::FeatureExtensionValue> extensionValue;
// Check parameters
diff --git a/platform/darwin/src/MGLSource.h b/platform/darwin/src/MGLSource.h
index 7bbf02fa7c..dc92e652e8 100644
--- a/platform/darwin/src/MGLSource.h
+++ b/platform/darwin/src/MGLSource.h
@@ -1,9 +1,12 @@
#import <Foundation/Foundation.h>
#import "MGLFoundation.h"
+#import "MGLTypes.h"
NS_ASSUME_NONNULL_BEGIN
+FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLInvalidStyleSourceException;
+
/**
`MGLSource` is an abstract base class for map content sources. A map content
source supplies content to be shown on the map. A source is added to an
diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm
index fd54627471..ef23fe30e7 100644
--- a/platform/darwin/src/MGLSource.mm
+++ b/platform/darwin/src/MGLSource.mm
@@ -7,6 +7,8 @@
#include <mbgl/map/map.hpp>
#include <mbgl/style/source.hpp>
+const MGLExceptionName MGLInvalidStyleSourceException = @"MGLInvalidStyleSourceException";
+
@interface MGLSource ()
// Even though this class is abstract, MGLStyle uses it to represent some
@@ -19,13 +21,14 @@
@implementation MGLSource {
std::unique_ptr<mbgl::style::Source> _pendingSource;
+ mapbox::base::WeakPtr<mbgl::style::Source> _weakSource;
}
- (instancetype)initWithIdentifier:(NSString *)identifier
{
if (self = [super init]) {
- _identifier = identifier;
+ _identifier = [identifier copy];
}
return self;
}
@@ -33,13 +36,18 @@
- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(MGLMapView *)mapView {
NSString *identifier = @(rawSource->getID().c_str());
if (self = [self initWithIdentifier:identifier]) {
- _rawSource = rawSource;
- _rawSource->peer = SourceWrapper { self };
+ _weakSource = rawSource->makeWeakPtr();
+ rawSource->peer = SourceWrapper { self };
_mapView = mapView;
}
return self;
}
+- (mbgl::style::Source *)rawSource
+{
+ return _weakSource.get();
+}
+
- (instancetype)initWithPendingSource:(std::unique_ptr<mbgl::style::Source>)pendingSource {
if (self = [self initWithRawSource:pendingSource.get() mapView:nil]) {
_pendingSource = std::move(pendingSource);
@@ -59,6 +67,7 @@
}
- (BOOL)removeFromMapView:(MGLMapView *)mapView error:(NSError * __nullable * __nullable)outError {
+ MGLAssertStyleSourceIsValid();
BOOL removed = NO;
if (self.rawSource == mapView.style.rawStyle->getSource(self.identifier.UTF8String)) {
diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h
index af14c11b90..4a7c31694d 100644
--- a/platform/darwin/src/MGLSource_Private.h
+++ b/platform/darwin/src/MGLSource_Private.h
@@ -2,6 +2,7 @@
#include <memory>
+
NS_ASSUME_NONNULL_BEGIN
namespace mbgl {
@@ -18,6 +19,20 @@ struct SourceWrapper {
__weak MGLSource *source;
};
+/**
+ Assert that the style source is valid.
+
+ This macro should be used at the beginning of any public-facing instance method
+ of `MGLSource` and its subclasses. For private methods, an assertion is more appropriate.
+ */
+#define MGLAssertStyleSourceIsValid() \
+do { \
+ if (!self.rawSource) { \
+ [NSException raise:MGLInvalidStyleSourceException \
+ format:@"This source got invalidated after the style change"]; \
+ } \
+} while (NO);
+
@class MGLMapView;
@interface MGLSource (Private)
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 5768a7aaa7..cab0eaae28 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -226,12 +226,23 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
MGLLogDebug(@"Removing source: %@", source);
if (!source.rawSource) {
- [NSException raise:NSInvalidArgumentException format:
- @"The source %@ cannot be removed from the style. "
- @"Make sure the source was created as a member of a concrete subclass of MGLSource.",
- source];
+ NSString *errorMessage = [NSString stringWithFormat:
+ @"The source %@ cannot be removed from the style. "
+ @"Make sure the source was created as a member of a concrete subclass of MGLSource."
+ @"Automatic re-addition of sources after style changes is not currently supported.",
+ source];
+
+ if (outError) {
+ *outError = [NSError errorWithDomain:MGLErrorDomain
+ code:MGLErrorCodeSourceCannotBeRemovedFromStyle
+ userInfo:@{ NSLocalizedDescriptionKey : errorMessage }];
+ return NO;
+ }
+ else {
+ [NSException raise:NSInvalidArgumentException format:@"%@", errorMessage];
+ }
}
-
+
return [source removeFromMapView:self.mapView error:outError];
}
diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs
index 9435e0d2ff..91ba813b17 100644
--- a/platform/darwin/src/MGLStyleLayer.h.ejs
+++ b/platform/darwin/src/MGLStyleLayer.h.ejs
@@ -19,38 +19,38 @@
NS_ASSUME_NONNULL_BEGIN
<% for (const property of layoutProperties) { -%>
-<% if (property.type == "enum") { -%>
+<% if (definesEnum(property, layoutProperties)) { -%>
/**
-<%- propertyDoc(property.name, property, type, 'enum').wrap(80, 1) %>
+<%- propertyDoc(enumName(property), property, type, 'enum').wrap(80, 1) %>
Values of this type are used in the `MGL<%- camelize(type) %>StyleLayer.<%- camelizeWithLeadingLowercase(property.name) %>`
property.
*/
-typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) {
+typedef NS_ENUM(NSUInteger, MGL<%- camelize(enumName(property)) %>) {
<% for (const value in property.values) { -%>
/**
-<%- propertyDoc(property.name, property.values[value], type, 'enum').wrap(80, 4+1) %>
+<%- propertyDoc(enumName(property), property.values[value], type, 'enum').wrap(80, 4+1) %>
*/
- MGL<%- camelize(property.name) %><%- camelize(value) %>,
+ MGL<%- camelize(enumName(property)) %><%- camelize(value) %>,
<% } -%>
};
<% } -%>
<% } -%>
<% for (const property of paintProperties) { -%>
-<% if (property.type == "enum") { -%>
+<% if (definesEnum(property, paintProperties)) { -%>
/**
-<%- propertyDoc(property.name, property, type, 'enum').wrap(80, 1) %>
+<%- propertyDoc(enumName(property), property, type, 'enum').wrap(80, 1) %>
- Values of this type are used in the `MGL<%- camelize(type) %>StyleLayer.<%- camelizeWithLeadingLowercase(property.name) %>`
+ Values of this type are used in the `MGL<%- camelize(type) %>StyleLayer.<%- camelizeWithLeadingLowercase(enumName(property)) %>`
property.
*/
-typedef NS_ENUM(NSUInteger, MGL<%- camelize(property.name) %>) {
+typedef NS_ENUM(NSUInteger, MGL<%- camelize(enumName(property)) %>) {
<% for (const value in property.values) { -%>
/**
-<%- propertyDoc(property.name, property.values[value], type, 'enum').wrap(80, 4+1) %>
+<%- propertyDoc(enumName(property), property.values[value], type, 'enum').wrap(80, 4+1) %>
*/
- MGL<%- camelize(property.name) %><%- camelize(value) %>,
+ MGL<%- camelize(enumName(property)) %><%- camelize(value) %>,
<% } -%>
};
@@ -180,17 +180,17 @@ which it is added.
<% for (let property of enumProperties) { -%>
/**
- Creates a new value object containing the given `MGL<%- camelize(property.name) %>` enumeration.
+ Creates a new value object containing the given `MGL<%- camelize(enumName(property)) %>` enumeration.
@param <%- objCName(property) %> The value for the new object.
@return A new value object that contains the enumeration value.
*/
-+ (instancetype)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %>;
++ (instancetype)valueWithMGL<%- camelize(enumName(property)) %>:(MGL<%- camelize(enumName(property)) %>)<%- objCName(property) %>;
/**
- The `MGL<%- camelize(property.name) %>` enumeration representation of the value.
+ The `MGL<%- camelize(enumName(property)) %>` enumeration representation of the value.
*/
-@property (readonly) MGL<%- camelize(property.name) %> MGL<%- camelize(property.name) %>Value;
+@property (readonly) MGL<%- camelize(enumName(property)) %> MGL<%- camelize(enumName(property)) %>Value;
<% } -%>
@end
diff --git a/platform/darwin/src/MGLStyleLayer.mm b/platform/darwin/src/MGLStyleLayer.mm
index 05885bc63e..7847cbb319 100644
--- a/platform/darwin/src/MGLStyleLayer.mm
+++ b/platform/darwin/src/MGLStyleLayer.mm
@@ -14,13 +14,14 @@ const MGLExceptionName MGLInvalidStyleLayerException = @"MGLInvalidStyleLayerExc
@implementation MGLStyleLayer {
std::unique_ptr<mbgl::style::Layer> _pendingLayer;
+ mapbox::base::WeakPtr<mbgl::style::Layer> _weakLayer;
}
- (instancetype)initWithRawLayer:(mbgl::style::Layer *)rawLayer {
if (self = [super init]) {
_identifier = @(rawLayer->getID().c_str());
- _rawLayer = rawLayer;
- _rawLayer->peer = LayerWrapper { self };
+ _weakLayer = rawLayer->makeWeakPtr();
+ rawLayer->peer = LayerWrapper { self };
}
return self;
}
@@ -32,6 +33,11 @@ const MGLExceptionName MGLInvalidStyleLayerException = @"MGLInvalidStyleLayerExc
return self;
}
+- (mbgl::style::Layer *)rawLayer
+{
+ return _weakLayer.get();
+}
+
- (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer
{
if (_pendingLayer == nullptr) {
@@ -103,9 +109,15 @@ const MGLExceptionName MGLInvalidStyleLayerException = @"MGLInvalidStyleLayerExc
- (NSString *)description
{
- return [NSString stringWithFormat:@"<%@: %p; identifier = %@; visible = %@>",
- NSStringFromClass([self class]), (void *)self, self.identifier,
- self.visible ? @"YES" : @"NO"];
+ if (self.rawLayer) {
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@; visible = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier,
+ self.visible ? @"YES" : @"NO"];
+ }
+ else {
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@; visible = NO>",
+ NSStringFromClass([self class]), (void *)self, self.identifier];
+ }
}
@end
diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs
index 26cb3e26f6..5d9f546cd7 100644
--- a/platform/darwin/src/MGLStyleLayer.mm.ejs
+++ b/platform/darwin/src/MGLStyleLayer.mm.ejs
@@ -25,10 +25,10 @@ namespace mbgl {
<% if (layoutProperties.length) { -%>
<% for (const property of layoutProperties) { -%>
-<% if (property.type == "enum") { -%>
- MBGL_DEFINE_ENUM(MGL<%- camelize(property.name) %>, {
+<% if (definesEnum(property, layoutProperties)) { -%>
+ MBGL_DEFINE_ENUM(MGL<%- camelize(enumName(property)) %>, {
<% for (const value in property.values) { -%>
- { MGL<%- camelize(property.name) %><%- camelize(value) %>, "<%-value%>" },
+ { MGL<%- camelize(enumName(property)) %><%- camelize(value) %>, "<%-value%>" },
<% } -%>
});
@@ -37,10 +37,10 @@ namespace mbgl {
<% } -%>
<% if (paintProperties.length) { -%>
<% for (const property of paintProperties) { -%>
-<% if (property.type == "enum") { -%>
- MBGL_DEFINE_ENUM(MGL<%- camelize(property.name) %>, {
+<% if (definesEnum(property, paintProperties)) { -%>
+ MBGL_DEFINE_ENUM(MGL<%- camelize(enumName(property)) %>, {
<% for (const value in property.values) { -%>
- { MGL<%- camelize(property.name) %><%- camelize(value) %>, "<%-value%>" },
+ { MGL<%- camelize(enumName(property)) %><%- camelize(value) %>, "<%-value%>" },
<% } -%>
});
@@ -239,12 +239,12 @@ namespace mbgl {
@implementation NSValue (MGL<%- camelize(type) %>StyleLayerAdditions)
<% for (let property of enumProperties) { -%>
-+ (NSValue *)valueWithMGL<%- camelize(property.name) %>:(MGL<%- camelize(property.name) %>)<%- objCName(property) %> {
- return [NSValue value:&<%- objCName(property) %> withObjCType:@encode(MGL<%- camelize(property.name) %>)];
++ (NSValue *)valueWithMGL<%- camelize(enumName(property)) %>:(MGL<%- camelize(enumName(property)) %>)<%- objCName(property) %> {
+ return [NSValue value:&<%- objCName(property) %> withObjCType:@encode(MGL<%- camelize(enumName(property)) %>)];
}
-- (MGL<%- camelize(property.name) %>)MGL<%- camelize(property.name) %>Value {
- MGL<%- camelize(property.name) %> <%- objCName(property) %>;
+- (MGL<%- camelize(enumName(property)) %>)MGL<%- camelize(enumName(property)) %>Value {
+ MGL<%- camelize(enumName(property)) %> <%- objCName(property) %>;
[self getValue:&<%- objCName(property) %>];
return <%- objCName(property) %>;
}
diff --git a/platform/darwin/src/MGLStyleLayer_Private.h b/platform/darwin/src/MGLStyleLayer_Private.h
index 314f61f680..52254f78c6 100644
--- a/platform/darwin/src/MGLStyleLayer_Private.h
+++ b/platform/darwin/src/MGLStyleLayer_Private.h
@@ -27,6 +27,7 @@ struct LayerWrapper {
if (!self.rawLayer) { \
[NSException raise:MGLInvalidStyleLayerException \
format: \
+ @"Either this layer got invalidated after the style change or " \
@"-[MGLStyle removeLayer:] has been called " \
@"with this instance but another style layer instance was added with the same identifer. It is an " \
@"error to send any message to this layer since it cannot be recovered after removal due to the " \
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index 658a31679e..53bc488a41 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -317,6 +317,30 @@ typedef NS_ENUM(NSUInteger, MGLTextTransform) {
};
/**
+ The property allows control over a symbol's orientation. Note that the property
+ values act as a hint, so that a symbol whose language doesn’t support the
+ provided orientation will be laid out in its natural orientation. Example:
+ English point symbol will be rendered horizontally even if array value contains
+ single 'vertical' enum value. The order of elements in an array define priority
+ order for the placement of an orientation variant.
+
+ Values of this type are used in the `MGLSymbolStyleLayer.textWritingModes`
+ property.
+ */
+typedef NS_ENUM(NSUInteger, MGLTextWritingMode) {
+ /**
+ If a text's language supports horizontal writing mode, symbols with point
+ placement would be laid out horizontally.
+ */
+ MGLTextWritingModeHorizontal,
+ /**
+ If a text's language supports vertical writing mode, symbols with point
+ placement would be laid out vertically.
+ */
+ MGLTextWritingModeVertical,
+};
+
+/**
Controls the frame of reference for `MGLSymbolStyleLayer.iconTranslation`.
Values of this type are used in the `MGLSymbolStyleLayer.iconTranslationAnchor`
@@ -1380,8 +1404,7 @@ MGL_EXPORT
ems downward. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`, and
- `textRadialOffset` is set to `nil`, and `textVariableAnchor` is set to `nil`.
- Otherwise, it is ignored.
+ `textRadialOffset` is set to `nil`. Otherwise, it is ignored.
You can set this property to an expression containing any of the following:
@@ -1404,8 +1427,7 @@ MGL_EXPORT
ems upward. Set this property to `nil` to reset it to the default value.
This property is only applied to the style if `text` is non-`nil`, and
- `textRadialOffset` is set to `nil`, and `textVariableAnchor` is set to `nil`.
- Otherwise, it is ignored.
+ `textRadialOffset` is set to `nil`. Otherwise, it is ignored.
You can set this property to an expression containing any of the following:
@@ -1497,8 +1519,8 @@ MGL_EXPORT
/**
Radial offset of text, in the direction of the symbol's anchor. Useful in
- combination with `textVariableAnchor`, which doesn't support the
- two-dimensional `textOffset`.
+ combination with `textVariableAnchor`, which defaults to using the
+ two-dimensional `textOffset` if present.
This property is measured in ems.
@@ -1610,8 +1632,7 @@ MGL_EXPORT
provide an array of `textAnchor` locations: the render will attempt to place
the label at each location, in order, before moving onto the next label. Use
`textJustify: auto` to choose justification based on anchor position. To apply
- an offset, use the `textRadialOffset` instead of the two-dimensional
- `textOffset`.
+ an offset, use the `textRadialOffset` or the two-dimensional `textOffset`.
This property is only applied to the style if `text` is non-`nil`, and
`symbolPlacement` is set to an expression that evaluates to or
@@ -1620,7 +1641,7 @@ MGL_EXPORT
You can set this property to an expression containing any of the following:
* Constant `MGLTextAnchor` array values
- * Constant array, whose each element is any of the following constant string
+ * Constant array, in which each element is any of the following constant string
values:
* `center`: The center of the text is placed closest to the anchor.
* `left`: The left side of the text is placed closest to the anchor.
@@ -1646,6 +1667,45 @@ MGL_EXPORT
*/
@property (nonatomic, null_resettable) NSExpression *textVariableAnchor;
+/**
+ The property allows control over a symbol's orientation. Note that the property
+ values act as a hint, so that a symbol whose language doesn’t support the
+ provided orientation will be laid out in its natural orientation. Example:
+ English point symbol will be rendered horizontally even if array value contains
+ single 'vertical' enum value. The order of elements in an array define priority
+ order for the placement of an orientation variant.
+
+ This property is only applied to the style if `text` is non-`nil`, and
+ `symbolPlacement` is set to an expression that evaluates to or
+ `MGLSymbolPlacementPoint`. Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-writing-mode"><code>text-writing-mode</code></a>
+ layout property in the Mapbox Style Specification.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `MGLTextWritingMode` array values
+ * Constant array, in which each element is any of the following constant string
+ values:
+ * `horizontal`: If a text's language supports horizontal writing mode,
+ symbols with point placement would be laid out horizontally.
+ * `vertical`: If a text's language supports vertical writing mode, symbols
+ with point placement would be laid out vertically.
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Step functions applied to the `$zoomLevel` variable
+
+ This property does not support applying interpolation functions to the
+ `$zoomLevel` variable or applying interpolation or step functions to feature
+ attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *textWritingModes;
+
+
+@property (nonatomic, null_resettable) NSExpression *textWritingMode __attribute__((unavailable("Use textWritingModes instead.")));
+
#pragma mark - Accessing the Paint Attributes
#if TARGET_OS_IPHONE
@@ -2383,6 +2443,19 @@ MGL_EXPORT
@property (readonly) MGLTextTransform MGLTextTransformValue;
/**
+ Creates a new value object containing the given `MGLTextWritingMode` enumeration.
+
+ @param textWritingModes The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLTextWritingMode:(MGLTextWritingMode)textWritingModes;
+
+/**
+ The `MGLTextWritingMode` enumeration representation of the value.
+ */
+@property (readonly) MGLTextWritingMode MGLTextWritingModeValue;
+
+/**
Creates a new value object containing the given `MGLIconTranslationAnchor` enumeration.
@param iconTranslationAnchor The value for the new object.
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index e54a0b9a15..e89b9c3e88 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -96,6 +96,11 @@ namespace mbgl {
{ MGLTextTransformLowercase, "lowercase" },
});
+ MBGL_DEFINE_ENUM(MGLTextWritingMode, {
+ { MGLTextWritingModeHorizontal, "horizontal" },
+ { MGLTextWritingModeVertical, "vertical" },
+ });
+
MBGL_DEFINE_ENUM(MGLIconTranslationAnchor, {
{ MGLIconTranslationAnchorMap, "map" },
{ MGLIconTranslationAnchorViewport, "viewport" },
@@ -1023,6 +1028,31 @@ namespace mbgl {
return MGLStyleValueTransformer<std::vector<mbgl::style::SymbolAnchorType>, NSArray<NSValue *> *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toExpression(propertyValue);
}
+- (void)setTextWritingModes:(NSExpression *)textWritingModes {
+ MGLAssertStyleLayerIsValid();
+ MGLLogDebug(@"Setting textWritingModes: %@", textWritingModes);
+
+ auto mbglValue = MGLStyleValueTransformer<std::vector<mbgl::style::TextWritingModeType>, NSArray<NSValue *> *, mbgl::style::TextWritingModeType, MGLTextWritingMode>().toPropertyValue<mbgl::style::PropertyValue<std::vector<mbgl::style::TextWritingModeType>>>(textWritingModes, false);
+ self.rawLayer->setTextWritingMode(mbglValue);
+}
+
+- (NSExpression *)textWritingModes {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getTextWritingMode();
+ if (propertyValue.isUndefined()) {
+ propertyValue = self.rawLayer->getDefaultTextWritingMode();
+ }
+ return MGLStyleValueTransformer<std::vector<mbgl::style::TextWritingModeType>, NSArray<NSValue *> *, mbgl::style::TextWritingModeType, MGLTextWritingMode>().toExpression(propertyValue);
+}
+
+- (void)setTextWritingMode:(NSExpression *)textWritingMode {
+}
+
+- (NSExpression *)textWritingMode {
+ return self.textWritingModes;
+}
+
#pragma mark - Accessing the Paint Attributes
- (void)setIconColor:(NSExpression *)iconColor {
@@ -1599,6 +1629,16 @@ namespace mbgl {
return textTransform;
}
++ (NSValue *)valueWithMGLTextWritingMode:(MGLTextWritingMode)textWritingModes {
+ return [NSValue value:&textWritingModes withObjCType:@encode(MGLTextWritingMode)];
+}
+
+- (MGLTextWritingMode)MGLTextWritingModeValue {
+ MGLTextWritingMode textWritingModes;
+ [self getValue:&textWritingModes];
+ return textWritingModes;
+}
+
+ (NSValue *)valueWithMGLIconTranslationAnchor:(MGLIconTranslationAnchor)iconTranslationAnchor {
return [NSValue value:&iconTranslationAnchor withObjCType:@encode(MGLIconTranslationAnchor)];
}
diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h
index 7e0dd27141..c0c93002fb 100644
--- a/platform/darwin/src/MGLTypes.h
+++ b/platform/darwin/src/MGLTypes.h
@@ -55,7 +55,9 @@ typedef NS_ENUM(NSInteger, MGLErrorCode) {
/** Source is in use and cannot be removed */
MGLErrorCodeSourceIdentifierMismatch = 8,
/** An error occurred while modifying the offline storage database */
- MGLErrorCodeModifyingOfflineStorageFailed = 9
+ MGLErrorCodeModifyingOfflineStorageFailed = 9,
+ /** Source is invalid and cannot be removed from the style (e.g. after a style change) */
+ MGLErrorCodeSourceCannotBeRemovedFromStyle = 10
};
/** Options for enabling debugging features in an `MGLMapView` instance. */
diff --git a/platform/darwin/src/MGLVectorStyleLayer.m b/platform/darwin/src/MGLVectorStyleLayer.m
deleted file mode 100644
index 23f3556e0b..0000000000
--- a/platform/darwin/src/MGLVectorStyleLayer.m
+++ /dev/null
@@ -1,25 +0,0 @@
-#import "MGLVectorStyleLayer.h"
-
-@implementation MGLVectorStyleLayer
-
-- (void)setPredicate:(NSPredicate *)predicate {
- [NSException raise:MGLAbstractClassException
- format:@"MGLVectorStyleLayer is an abstract class"];
-}
-
-- (NSPredicate *)predicate {
- [NSException raise:MGLAbstractClassException
- format:@"MGLVectorStyleLayer is an abstract class"];
- return nil;
-}
-
-- (NSString *)description {
- return [NSString stringWithFormat:
- @"<%@: %p; identifier = %@; sourceIdentifier = %@; "
- @"sourceLayerIdentifier = %@; predicate = %@; visible = %@>",
- NSStringFromClass([self class]), (void *)self, self.identifier,
- self.sourceIdentifier, self.sourceLayerIdentifier, self.predicate,
- self.visible ? @"YES" : @"NO"];
-}
-
-@end
diff --git a/platform/darwin/src/MGLVectorStyleLayer.mm b/platform/darwin/src/MGLVectorStyleLayer.mm
new file mode 100644
index 0000000000..691668629a
--- /dev/null
+++ b/platform/darwin/src/MGLVectorStyleLayer.mm
@@ -0,0 +1,34 @@
+#import "MGLVectorStyleLayer.h"
+#import "MGLStyleLayer_Private.h"
+
+@implementation MGLVectorStyleLayer
+
+- (void)setPredicate:(NSPredicate *)predicate {
+ [NSException raise:MGLAbstractClassException
+ format:@"MGLVectorStyleLayer is an abstract class"];
+}
+
+- (NSPredicate *)predicate {
+ [NSException raise:MGLAbstractClassException
+ format:@"MGLVectorStyleLayer is an abstract class"];
+ return nil;
+}
+
+- (NSString *)description {
+ if (self.rawLayer) {
+ return [NSString stringWithFormat:
+ @"<%@: %p; identifier = %@; sourceIdentifier = %@; "
+ @"sourceLayerIdentifier = %@; predicate = %@; visible = %@>",
+ NSStringFromClass([self class]), (void *)self, self.identifier,
+ self.sourceIdentifier, self.sourceLayerIdentifier, self.predicate,
+ self.visible ? @"YES" : @"NO"];
+ }
+ else {
+ return [NSString stringWithFormat:
+ @"<%@: %p; identifier = %@; sourceIdentifier = <unknown>; "
+ @"sourceLayerIdentifier = <unknown>; predicate = <unknown>; visible = <unknown>>",
+ NSStringFromClass([self class]), (void *)self, self.identifier];
+ }
+}
+
+@end
diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm
index f7a6869ade..85270c4a49 100644
--- a/platform/darwin/src/MGLVectorTileSource.mm
+++ b/platform/darwin/src/MGLVectorTileSource.mm
@@ -38,17 +38,19 @@
}
- (NSURL *)configurationURL {
+ MGLAssertStyleSourceIsValid();
auto url = self.rawSource->getURL();
return url ? [NSURL URLWithString:@(url->c_str())] : nil;
}
- (NSString *)attributionHTMLString {
+ MGLAssertStyleSourceIsValid();
auto attribution = self.rawSource->getAttribution();
return attribution ? @(attribution->c_str()) : nil;
}
- (NSArray<id <MGLFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate {
-
+ MGLAssertStyleSourceIsValid();
mbgl::optional<std::vector<std::string>> optionalSourceLayerIDs;
if (sourceLayerIdentifiers) {
__block std::vector<std::string> layerIDs;
diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm
index 79810546b3..b0f69f4a7f 100644
--- a/platform/darwin/src/http_file_source.mm
+++ b/platform/darwin/src/http_file_source.mm
@@ -88,15 +88,14 @@ class HTTPFileSource::Impl {
public:
Impl() {
@autoreleasepool {
-
- NSURLSessionConfiguration *sessionConfig =
- [MGLNetworkConfiguration sharedManager].sessionConfiguration;
-
+ NSURLSessionConfiguration *sessionConfig = [MGLNetworkConfiguration sharedManager].sessionConfiguration;
session = [NSURLSession sessionWithConfiguration:sessionConfig];
userAgent = getUserAgent();
- accountType = [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"];
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+ accountType = [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountTypeKey];
+#endif
}
}
@@ -205,13 +204,13 @@ NSURL *resourceURLWithAccountType(const Resource& resource, NSInteger accountTyp
if (accountType == 0 &&
([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"])) {
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
- NSURLQueryItem *accountsQueryItem = [NSURLQueryItem queryItemWithName:@"sku" value:MGLAccountManager.skuToken];
-
- NSMutableArray *queryItems = [NSMutableArray arrayWithObject:accountsQueryItem];
-
- // offline here
+ NSMutableArray *queryItems = [NSMutableArray array];
+
if (resource.usage == Resource::Usage::Offline) {
[queryItems addObject:[NSURLQueryItem queryItemWithName:@"offline" value:@"true"]];
+ } else {
+ // Only add SKU token to requests not tagged as "offline" usage.
+ [queryItems addObject:[NSURLQueryItem queryItemWithName:@"sku" value:MGLAccountManager.skuToken]];
}
if (components.queryItems) {
@@ -245,8 +244,10 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
}
[req addValue:impl->userAgent forHTTPHeaderField:@"User-Agent"];
-
- if (resource.kind == mbgl::Resource::Kind::Tile) {
+
+ const bool isTile = resource.kind == mbgl::Resource::Kind::Tile;
+
+ if (isTile) {
[[MGLNetworkConfiguration sharedManager] startDownloadEvent:url.relativePath type:@"tile"];
}
@@ -322,7 +323,7 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
if (responseCode == 200) {
response.data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
- } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) {
+ } else if (responseCode == 204 || (responseCode == 404 && isTile)) {
response.noContent = true;
} else if (responseCode == 304) {
response.notModified = true;
diff --git a/platform/darwin/src/string_nsstring.mm b/platform/darwin/src/string_nsstring.mm
index 096ed2b212..382a2acf33 100644
--- a/platform/darwin/src/string_nsstring.mm
+++ b/platform/darwin/src/string_nsstring.mm
@@ -43,6 +43,10 @@ std::string formatNumber(double number, const std::string& localeId, const std::
numberFormatter.maximumFractionDigits = maxFractionDigits;
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
} else {
+ // Reset fraction digits to NSNumberFormatter's default values, so that the
+ // system will choose formatting based on number formatter locale.
+ numberFormatter.minimumFractionDigits = 0;
+ numberFormatter.maximumFractionDigits = 0;
numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
}
NSString *formatted = [numberFormatter stringFromNumber:@(number)];
diff --git a/platform/darwin/test/MGLClockDirectionFormatterTests.m b/platform/darwin/test/MGLClockDirectionFormatterTests.m
index 1b2f058d18..05ebc3af2d 100644
--- a/platform/darwin/test/MGLClockDirectionFormatterTests.m
+++ b/platform/darwin/test/MGLClockDirectionFormatterTests.m
@@ -7,11 +7,6 @@
@implementation MGLClockDirectionFormatterTests
-- (void)setUp {
- // FIXME: https://github.com/mapbox/mapbox-gl-native/issues/14908
- XCTAssertEqualObjects(NSLocale.currentLocale.localeIdentifier, @"en_US", @"Device locale must be en_US for these tests to pass.");
-}
-
- (void)testClockDirections {
MGLClockDirectionFormatter *shortFormatter = [[MGLClockDirectionFormatter alloc] init];
shortFormatter.unitStyle = NSFormattingUnitStyleShort;
diff --git a/platform/darwin/test/MGLCompassDirectionFormatterTests.m b/platform/darwin/test/MGLCompassDirectionFormatterTests.m
index cd3aaebab7..c4ccc6ac4f 100644
--- a/platform/darwin/test/MGLCompassDirectionFormatterTests.m
+++ b/platform/darwin/test/MGLCompassDirectionFormatterTests.m
@@ -7,11 +7,6 @@
@implementation MGLCompassDirectionFormatterTests
-- (void)setUp {
- // FIXME: https://github.com/mapbox/mapbox-gl-native/issues/14908
- XCTAssertEqualObjects(NSLocale.currentLocale.localeIdentifier, @"en_US", @"Device locale must be en_US for these tests to pass.");
-}
-
- (void)testCompassDirections {
MGLCompassDirectionFormatter *shortFormatter = [[MGLCompassDirectionFormatter alloc] init];
shortFormatter.unitStyle = NSFormattingUnitStyleShort;
diff --git a/platform/darwin/test/MGLCoordinateFormatterTests.m b/platform/darwin/test/MGLCoordinateFormatterTests.m
index 4d4d8cf971..ac083fa103 100644
--- a/platform/darwin/test/MGLCoordinateFormatterTests.m
+++ b/platform/darwin/test/MGLCoordinateFormatterTests.m
@@ -7,11 +7,6 @@
@implementation MGLCoordinateFormatterTests
-- (void)setUp {
- // FIXME: https://github.com/mapbox/mapbox-gl-native/issues/14908
- XCTAssertEqualObjects(NSLocale.currentLocale.localeIdentifier, @"en_US", @"Device locale must be en_US for these tests to pass.");
-}
-
- (void)testStrings {
MGLCoordinateFormatter *shortFormatter = [[MGLCoordinateFormatter alloc] init];
shortFormatter.unitStyle = NSFormattingUnitStyleShort;
diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
index 2a64fbc601..9fbb0cc329 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -1,10 +1,5 @@
import XCTest
import Mapbox
-#if os(iOS)
- import UIKit
-#else
- import Cocoa
-#endif
/**
Test cases that ensure the inline examples in the project documentation
diff --git a/platform/darwin/test/MGLDocumentationGuideTests.swift b/platform/darwin/test/MGLDocumentationGuideTests.swift
index b1e2bb843f..30814c4156 100644
--- a/platform/darwin/test/MGLDocumentationGuideTests.swift
+++ b/platform/darwin/test/MGLDocumentationGuideTests.swift
@@ -1,11 +1,5 @@
import XCTest
-import Foundation
import Mapbox
-#if os(iOS)
- import UIKit
-#else
- import Cocoa
-#endif
/**
Test cases that ensure the inline examples in the jazzy guides compile.
diff --git a/platform/darwin/test/MGLResourceTests.mm b/platform/darwin/test/MGLResourceTests.mm
index 0420997c39..7fcccc535c 100644
--- a/platform/darwin/test/MGLResourceTests.mm
+++ b/platform/darwin/test/MGLResourceTests.mm
@@ -22,18 +22,28 @@ namespace mbgl {
NSURL *url = [NSURL URLWithString:@(testURL.c_str())];
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
NSArray<NSURLQueryItem *> *items = components.queryItems;
- XCTAssert(items.count == 2 );
+ XCTAssert(items.count == 2);
}
Resource resource(Resource::Kind::Unknown, testURL);
- // By default, resource are NOT offline
+ // By default, resources are NOT offline
{
+ bool skuTokenQueryItemFound;
NSURL *url = resourceURLWithAccountType(resource, 0);
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
for (NSURLQueryItem *item in components.queryItems) {
XCTAssertFalse([item.name isEqualToString:@"offline"]);
+ if ([item.name isEqualToString:@"sku"]) {
+ skuTokenQueryItemFound = YES;
+ }
}
+
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+ XCTAssertTrue(skuTokenQueryItemFound, "Default resource URL should have SKU token query item");
+#else
+ XCTAssertFalse(skuTokenQueryItemFound, "Non-iOS platforms should not have a SKU token query item");
+#endif
}
// Now check offline
@@ -43,20 +53,20 @@ namespace mbgl {
NSURL *url = resourceURLWithAccountType(resource, 0);
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
- // For offline, we expect a single offline param and a sku param
+ // For offline, we expect a single offline query item
NSInteger foundCount = 0;
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
for (NSURLQueryItem *item in components.queryItems) {
if (([item.name isEqualToString:@"offline"] && [item.value isEqualToString:@"true"]) ||
([item.name isEqualToString:@"a"] && [item.value isEqualToString:@"one"]) ||
- ([item.name isEqualToString:@"b"] && [item.value isEqualToString:@"two"]) ||
- ([item.name isEqualToString:@"sku"])) {
+ ([item.name isEqualToString:@"b"] && [item.value isEqualToString:@"two"])) {
foundCount++;
}
+ XCTAssertFalse([item.name isEqualToString:@"sku"]);
}
- XCTAssert(foundCount == 4);
+ XCTAssert(foundCount == 3);
#else
// NOTE: Currently the macOS SDK does not supply the sku or offline query parameters
for (NSURLQueryItem *item in components.queryItems) {
@@ -64,6 +74,7 @@ namespace mbgl {
([item.name isEqualToString:@"b"] && [item.value isEqualToString:@"two"])) {
foundCount++;
}
+ XCTAssertFalse([item.name isEqualToString:@"sku"]);
}
XCTAssert(foundCount == 2);
diff --git a/platform/darwin/test/MGLSDKTestHelpers.swift b/platform/darwin/test/MGLSDKTestHelpers.swift
index 1d174b37bc..c836f7c2fd 100644
--- a/platform/darwin/test/MGLSDKTestHelpers.swift
+++ b/platform/darwin/test/MGLSDKTestHelpers.swift
@@ -1,5 +1,4 @@
import XCTest
-import Foundation
class MGLSDKTestHelpers {
diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
index 4a38070007..14c7c397d3 100644
--- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs
+++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
@@ -214,7 +214,7 @@
<% for (let property of enumProperties) { -%>
<% for (let value in property.values) { -%>
<% if (property.values.hasOwnProperty(value)) { -%>
- XCTAssertEqual([NSValue valueWithMGL<%- camelize(property.name) %>:MGL<%- camelize(property.name) %><%- camelize(value) %>].MGL<%- camelize(property.name) %>Value, MGL<%- camelize(property.name) %><%- camelize(value) %>);
+ XCTAssertEqual([NSValue valueWithMGL<%- camelize(enumName(property)) %>:MGL<%- camelize(enumName(property)) %><%- camelize(value) %>].MGL<%- camelize(enumName(property)) %>Value, MGL<%- camelize(enumName(property)) %><%- camelize(value) %>);
<% } -%>
<% } -%>
<% } -%>
diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
index 87091acc1b..5cc9c3c332 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -2074,6 +2074,50 @@
XCTAssertThrowsSpecificNamed(layer.textVariableAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
}
+ // text-writing-mode
+ {
+ XCTAssertTrue(rawLayer->getTextWritingMode().isUndefined(),
+ @"text-writing-mode should be unset initially.");
+ NSExpression *defaultExpression = layer.textWritingModes;
+
+ NSExpression *constantExpression = [NSExpression expressionWithFormat:@"{'horizontal','vertical'}"];
+ layer.textWritingModes = constantExpression;
+ mbgl::style::PropertyValue<std::vector<mbgl::style::TextWritingModeType>> propertyValue = { { mbgl::style::TextWritingModeType::Horizontal, mbgl::style::TextWritingModeType::Vertical } };
+ XCTAssertEqual(rawLayer->getTextWritingMode(), propertyValue,
+ @"Setting textWritingModes to a constant value expression should update text-writing-mode.");
+ XCTAssertEqualObjects(layer.textWritingModes, constantExpression,
+ @"textWritingModes should round-trip constant value expressions.");
+
+ constantExpression = [NSExpression expressionWithFormat:@"{'horizontal','vertical'}"];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
+ layer.textWritingModes = functionExpression;
+
+ {
+ using namespace mbgl::style::expression::dsl;
+ propertyValue = mbgl::style::PropertyExpression<std::vector<mbgl::style::TextWritingModeType>>(
+ step(zoom(), literal({"horizontal", "vertical"}), 18.0, literal({"horizontal", "vertical"}))
+ );
+ }
+
+ XCTAssertEqual(rawLayer->getTextWritingMode(), propertyValue,
+ @"Setting textWritingModes to a camera expression should update text-writing-mode.");
+ XCTAssertEqualObjects(layer.textWritingModes, functionExpression,
+ @"textWritingModes should round-trip camera expressions.");
+
+
+ layer.textWritingModes = nil;
+ XCTAssertTrue(rawLayer->getTextWritingMode().isUndefined(),
+ @"Unsetting textWritingModes should return text-writing-mode to the default value.");
+ XCTAssertEqualObjects(layer.textWritingModes, defaultExpression,
+ @"textWritingModes should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
+ XCTAssertThrowsSpecificNamed(layer.textWritingModes = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
+ XCTAssertThrowsSpecificNamed(layer.textWritingModes = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes.");
+ }
+
// icon-color
{
XCTAssertTrue(rawLayer->getIconColor().isUndefined(),
@@ -3084,6 +3128,7 @@
[self testPropertyName:@"text-rotation-alignment" isBoolean:NO];
[self testPropertyName:@"text-transform" isBoolean:NO];
[self testPropertyName:@"text-variable-anchor" isBoolean:NO];
+ [self testPropertyName:@"text-writing-modes" isBoolean:NO];
[self testPropertyName:@"icon-color" isBoolean:NO];
[self testPropertyName:@"icon-halo-blur" isBoolean:NO];
[self testPropertyName:@"icon-halo-color" isBoolean:NO];
@@ -3148,6 +3193,8 @@
XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformNone].MGLTextTransformValue, MGLTextTransformNone);
XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformUppercase].MGLTextTransformValue, MGLTextTransformUppercase);
XCTAssertEqual([NSValue valueWithMGLTextTransform:MGLTextTransformLowercase].MGLTextTransformValue, MGLTextTransformLowercase);
+ XCTAssertEqual([NSValue valueWithMGLTextWritingMode:MGLTextWritingModeHorizontal].MGLTextWritingModeValue, MGLTextWritingModeHorizontal);
+ XCTAssertEqual([NSValue valueWithMGLTextWritingMode:MGLTextWritingModeVertical].MGLTextWritingModeValue, MGLTextWritingModeVertical);
XCTAssertEqual([NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorMap].MGLIconTranslationAnchorValue, MGLIconTranslationAnchorMap);
XCTAssertEqual([NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorViewport].MGLIconTranslationAnchorValue, MGLIconTranslationAnchorViewport);
XCTAssertEqual([NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorMap].MGLTextTranslationAnchorValue, MGLTextTranslationAnchorMap);
diff --git a/platform/darwin/test/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm
index 998d4baee6..65e096ae7f 100644
--- a/platform/darwin/test/MGLTileSetTests.mm
+++ b/platform/darwin/test/MGLTileSetTests.mm
@@ -84,9 +84,18 @@
// the attribution is reflected by the mbgl tileset
#if TARGET_OS_IPHONE
- NSString *html = (@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00pt\">"
- @"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
- @"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00pt; background-color: #ff0000\">GL</font>\n");
+ NSString *html;
+ if (@available(iOS 13.0, *)) {
+ // TODO: investigate visual impact
+ // iOS 13 evidently changes font size from points to pixels
+ html = (@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00px\">"
+ @"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
+ @"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00px; background-color: #ff0000\">GL</font>\n");
+ } else {
+ html = (@"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00pt\">"
+ @"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
+ @"<font style=\"font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 12.00pt; background-color: #ff0000\">GL</font>\n");
+ }
#else
NSString *html = (@"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica\">"
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
@@ -111,7 +120,7 @@
// the scheme is reflected by the mbgl tileset
XCTAssertEqual(tileSet.scheme, mbgl::Tileset::Scheme::TMS);
- // when the dem enciding is changed using an NSNumber
+ // when the dem encoding is changed using an NSNumber
tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{
MGLTileSourceOptionDEMEncoding: @(MGLDEMEncodingTerrarium),
});
@@ -119,7 +128,7 @@
// the encoding is reflected by the mbgl tileset
XCTAssertEqual(tileSet.encoding, mbgl::Tileset::DEMEncoding::Terrarium);
- // when the dem enciding is changed using an NSValue
+ // when the dem encoding is changed using an NSValue
MGLDEMEncoding terrarium = MGLDEMEncodingTerrarium;
tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{
MGLTileSourceOptionDEMEncoding: [NSValue value:&terrarium withObjCType:@encode(MGLDEMEncoding)],
diff --git a/platform/default/include/mbgl/gfx/headless_frontend.hpp b/platform/default/include/mbgl/gfx/headless_frontend.hpp
index 7f937b34b8..8f7a7bf202 100644
--- a/platform/default/include/mbgl/gfx/headless_frontend.hpp
+++ b/platform/default/include/mbgl/gfx/headless_frontend.hpp
@@ -17,12 +17,10 @@ class TransformState;
class HeadlessFrontend : public RendererFrontend {
public:
HeadlessFrontend(float pixelRatio_,
- const optional<std::string> programCacheDir = {},
gfx::ContextMode mode = gfx::ContextMode::Unique,
const optional<std::string> localFontFamily = {});
HeadlessFrontend(Size,
float pixelRatio_,
- const optional<std::string> programCacheDir = {},
gfx::ContextMode mode = gfx::ContextMode::Unique,
const optional<std::string> localFontFamily = {});
~HeadlessFrontend() override;
diff --git a/platform/default/include/mbgl/map/map_snapshotter.hpp b/platform/default/include/mbgl/map/map_snapshotter.hpp
index db1ba1f267..c1544c36ab 100644
--- a/platform/default/include/mbgl/map/map_snapshotter.hpp
+++ b/platform/default/include/mbgl/map/map_snapshotter.hpp
@@ -31,7 +31,6 @@ public:
const float pixelRatio,
const optional<CameraOptions> cameraOptions,
const optional<LatLngBounds> region,
- const optional<std::string> cacheDir,
const optional<std::string> localFontFamily,
const ResourceOptions&);
diff --git a/platform/default/include/mbgl/storage/offline_database.hpp b/platform/default/include/mbgl/storage/offline_database.hpp
index afce87b542..e19dcfade9 100644
--- a/platform/default/include/mbgl/storage/offline_database.hpp
+++ b/platform/default/include/mbgl/storage/offline_database.hpp
@@ -78,8 +78,8 @@ public:
std::exception_ptr invalidateRegion(int64_t regionID);
// Return value is (response, stored size)
- optional<std::pair<Response, uint64_t>> getRegionResource(int64_t regionID, const Resource&);
- optional<int64_t> hasRegionResource(int64_t regionID, const Resource&);
+ optional<std::pair<Response, uint64_t>> getRegionResource(const Resource&);
+ optional<int64_t> hasRegionResource(const Resource&);
uint64_t putRegionResource(int64_t regionID, const Resource&, const Response&);
void putRegionResources(int64_t regionID, const std::list<std::tuple<Resource, Response>>&, OfflineRegionStatus&);
@@ -92,6 +92,7 @@ public:
bool offlineMapboxTileCountLimitExceeded();
uint64_t getOfflineMapboxTileCount();
bool exceedsOfflineMapboxTileCountLimit(const Resource&);
+ void markUsedResources(int64_t regionID, const std::list<Resource>&);
private:
void initialize();
diff --git a/platform/default/include/mbgl/storage/offline_download.hpp b/platform/default/include/mbgl/storage/offline_download.hpp
index 84b319fb5a..53b42ae9d1 100644
--- a/platform/default/include/mbgl/storage/offline_download.hpp
+++ b/platform/default/include/mbgl/storage/offline_download.hpp
@@ -46,7 +46,7 @@ private:
* While the request is in progress, it is recorded in `requests`. If the download
* is deactivated, all in progress requests are cancelled.
*/
- void ensureResource(const Resource&, std::function<void (Response)> = {});
+ void ensureResource(Resource&&, std::function<void (Response)> = {});
void onMapboxTileCountLimitExceeded();
@@ -60,10 +60,12 @@ private:
std::list<std::unique_ptr<AsyncRequest>> requests;
std::unordered_set<std::string> requiredSourceURLs;
std::deque<Resource> resourcesRemaining;
+ std::list<Resource> resourcesToBeMarkedAsUsed;
std::list<std::tuple<Resource, Response>> buffer;
void queueResource(Resource&&);
void queueTiles(style::SourceType, uint16_t tileSize, const Tileset&);
+ void markPendingUsedResources();
};
} // namespace mbgl
diff --git a/platform/default/src/mbgl/gfx/headless_frontend.cpp b/platform/default/src/mbgl/gfx/headless_frontend.cpp
index f5e6b7f482..287567adbd 100644
--- a/platform/default/src/mbgl/gfx/headless_frontend.cpp
+++ b/platform/default/src/mbgl/gfx/headless_frontend.cpp
@@ -10,16 +10,14 @@
namespace mbgl {
HeadlessFrontend::HeadlessFrontend(float pixelRatio_,
- const optional<std::string> programCacheDir,
const gfx::ContextMode contextMode,
const optional<std::string> localFontFamily)
: HeadlessFrontend(
- { 256, 256 }, pixelRatio_, programCacheDir, contextMode, localFontFamily) {
+ { 256, 256 }, pixelRatio_, contextMode, localFontFamily) {
}
HeadlessFrontend::HeadlessFrontend(Size size_,
float pixelRatio_,
- const optional<std::string> programCacheDir,
const gfx::ContextMode contextMode,
const optional<std::string> localFontFamily)
: size(size_),
@@ -38,7 +36,7 @@ HeadlessFrontend::HeadlessFrontend(Size size_,
renderer->render(*updateParameters_);
}
}),
- renderer(std::make_unique<Renderer>(*getBackend(), pixelRatio, programCacheDir, localFontFamily)) {
+ renderer(std::make_unique<Renderer>(*getBackend(), pixelRatio, localFontFamily)) {
}
HeadlessFrontend::~HeadlessFrontend() = default;
diff --git a/platform/default/src/mbgl/map/map_snapshotter.cpp b/platform/default/src/mbgl/map/map_snapshotter.cpp
index f0b107e77e..5f4060e3f0 100644
--- a/platform/default/src/mbgl/map/map_snapshotter.cpp
+++ b/platform/default/src/mbgl/map/map_snapshotter.cpp
@@ -19,7 +19,6 @@ public:
const float pixelRatio,
const optional<CameraOptions> cameraOptions,
const optional<LatLngBounds> region,
- const optional<std::string> programCacheDir,
const optional<std::string> localFontFamily,
const ResourceOptions& resourceOptions);
@@ -50,11 +49,10 @@ MapSnapshotter::Impl::Impl(const std::pair<bool, std::string> style,
const float pixelRatio,
const optional<CameraOptions> cameraOptions,
const optional<LatLngBounds> region,
- const optional<std::string> programCacheDir,
const optional<std::string> localFontFamily,
const ResourceOptions& resourceOptions)
: frontend(
- size, pixelRatio, programCacheDir, gfx::ContextMode::Unique, localFontFamily),
+ size, pixelRatio, gfx::ContextMode::Unique, localFontFamily),
map(frontend,
MapObserver::nullObserver(),
MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio),
@@ -168,12 +166,11 @@ MapSnapshotter::MapSnapshotter(const std::pair<bool, std::string> style,
const float pixelRatio,
const optional<CameraOptions> cameraOptions,
const optional<LatLngBounds> region,
- const optional<std::string> programCacheDir,
const optional<std::string> localFontFamily,
const ResourceOptions& resourceOptions)
: impl(std::make_unique<util::Thread<MapSnapshotter::Impl>>(
"Map Snapshotter", style, size, pixelRatio, cameraOptions,
- region, programCacheDir, localFontFamily, resourceOptions.clone())) {}
+ region, localFontFamily, resourceOptions.clone())) {}
MapSnapshotter::~MapSnapshotter() = default;
diff --git a/platform/default/src/mbgl/storage/http_file_source.cpp b/platform/default/src/mbgl/storage/http_file_source.cpp
index 213b53de98..b0390c58fc 100644
--- a/platform/default/src/mbgl/storage/http_file_source.cpp
+++ b/platform/default/src/mbgl/storage/http_file_source.cpp
@@ -13,85 +13,7 @@
#include <curl/curl.h>
-// Dynamically load all cURL functions. Debian-derived systems upgraded the OpenSSL version linked
-// to in https://salsa.debian.org/debian/curl/commit/95c94957bb7e89e36e78b995fed468c42f64d18d
-// They state:
-// Rename libcurl3 to libcurl4, because libcurl exposes an SSL_CTX via
-// CURLOPT_SSL_CTX_FUNCTION, and this object changes incompatibly between
-// openssl 1.0 and openssl 1.1.
-// Since we are not accessing the underlying OpenSSL context, we don't care whether we're linking
-// against libcurl3 or libcurl4; both use the ABI version 4 which hasn't changed since 2006
-// (see https://curl.haxx.se/libcurl/abi.html). In fact, cURL's ABI compatibility is very good as
-// shown on https://abi-laboratory.pro/tracker/timeline/curl/
-// Therefore, we're dynamically loading the cURL symbols we need to avoid linking against versioned
-// symbols.
#include <dlfcn.h>
-
-namespace curl {
-
-#define CURL_FUNCTIONS \
- X(global_init) \
- X(getdate) \
- X(easy_strerror) \
- X(easy_init) \
- X(easy_setopt) \
- X(easy_cleanup) \
- X(easy_getinfo) \
- X(easy_reset) \
- X(multi_init) \
- X(multi_add_handle) \
- X(multi_remove_handle) \
- X(multi_cleanup) \
- X(multi_info_read) \
- X(multi_strerror) \
- X(multi_socket_action) \
- X(multi_setopt) \
- X(share_init) \
- X(share_cleanup) \
- X(slist_append) \
- X(slist_free_all)
-
-#define X(name) static decltype(&curl_ ## name) name = nullptr;
-CURL_FUNCTIONS
-#undef X
-
-static void* handle = nullptr;
-
-static void* load(const char* name) {
- void* symbol = dlsym(handle, name);
- if (const char* error = dlerror()) {
- fprintf(stderr, "Cannot load symbol '%s': %s\n", name, error);
- dlclose(handle);
- handle = nullptr;
- abort();
- }
- return symbol;
-}
-
-__attribute__((constructor))
-static void load() {
- assert(!handle);
- handle = dlopen("libcurl.so.4", RTLD_LAZY | RTLD_LOCAL);
- if (!handle) {
- fprintf(stderr, "Could not open shared library '%s'\n", "libcurl.so.4");
- abort();
- }
-
- #define X(name) name = (decltype(&curl_ ## name))load("curl_" #name);
- CURL_FUNCTIONS
- #undef X
-}
-
-__attribute__((constructor))
-static void unload() {
- if (handle) {
- dlclose(handle);
- }
-}
-
-} // namespace curl
-
-
#include <queue>
#include <map>
#include <cassert>
@@ -100,13 +22,13 @@ static void unload() {
static void handleError(CURLMcode code) {
if (code != CURLM_OK) {
- throw std::runtime_error(std::string("CURL multi error: ") + curl::multi_strerror(code));
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(code));
}
}
static void handleError(CURLcode code) {
if (code != CURLE_OK) {
- throw std::runtime_error(std::string("CURL easy error: ") + curl::easy_strerror(code));
+ throw std::runtime_error(std::string("CURL easy error: ") + curl_easy_strerror(code));
}
}
@@ -170,29 +92,29 @@ private:
};
HTTPFileSource::Impl::Impl() {
- if (curl::global_init(CURL_GLOBAL_ALL)) {
+ if (curl_global_init(CURL_GLOBAL_ALL)) {
throw std::runtime_error("Could not init cURL");
}
- share = curl::share_init();
+ share = curl_share_init();
- multi = curl::multi_init();
- handleError(curl::multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handleSocket));
- handleError(curl::multi_setopt(multi, CURLMOPT_SOCKETDATA, this));
- handleError(curl::multi_setopt(multi, CURLMOPT_TIMERFUNCTION, startTimeout));
- handleError(curl::multi_setopt(multi, CURLMOPT_TIMERDATA, this));
+ multi = curl_multi_init();
+ handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handleSocket));
+ handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, this));
+ handleError(curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, startTimeout));
+ handleError(curl_multi_setopt(multi, CURLMOPT_TIMERDATA, this));
}
HTTPFileSource::Impl::~Impl() {
while (!handles.empty()) {
- curl::easy_cleanup(handles.front());
+ curl_easy_cleanup(handles.front());
handles.pop();
}
- curl::multi_cleanup(multi);
+ curl_multi_cleanup(multi);
multi = nullptr;
- curl::share_cleanup(share);
+ curl_share_cleanup(share);
share = nullptr;
timeout.stop();
@@ -204,12 +126,12 @@ CURL *HTTPFileSource::Impl::getHandle() {
handles.pop();
return handle;
} else {
- return curl::easy_init();
+ return curl_easy_init();
}
}
void HTTPFileSource::Impl::returnHandle(CURL *handle) {
- curl::easy_reset(handle);
+ curl_easy_reset(handle);
handles.push(handle);
}
@@ -217,11 +139,11 @@ void HTTPFileSource::Impl::checkMultiInfo() {
CURLMsg *message = nullptr;
int pending = 0;
- while ((message = curl::multi_info_read(multi, &pending))) {
+ while ((message = curl_multi_info_read(multi, &pending))) {
switch (message->msg) {
case CURLMSG_DONE: {
HTTPRequest *baton = nullptr;
- curl::easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
+ curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
assert(baton);
baton->handleResult(message->data.result);
} break;
@@ -245,7 +167,7 @@ void HTTPFileSource::Impl::perform(curl_socket_t s, util::RunLoop::Event events)
int running_handles = 0;
- curl::multi_socket_action(multi, s, flags, &running_handles);
+ curl_multi_socket_action(multi, s, flags, &running_handles);
checkMultiInfo();
}
@@ -279,9 +201,9 @@ int HTTPFileSource::Impl::handleSocket(CURL * /* handle */, curl_socket_t s, int
void HTTPFileSource::Impl::onTimeout(Impl *context) {
int running_handles;
- CURLMcode error = curl::multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
+ CURLMcode error = curl_multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
if (error != CURLM_OK) {
- throw std::runtime_error(std::string("CURL multi error: ") + curl::multi_strerror(error));
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error));
}
context->checkMultiInfo();
}
@@ -312,45 +234,45 @@ HTTPRequest::HTTPRequest(HTTPFileSource::Impl* context_, Resource resource_, Fil
// getting a 304 response if possible. This avoids redownloading unchanged data.
if (resource.priorEtag) {
const std::string header = std::string("If-None-Match: ") + *resource.priorEtag;
- headers = curl::slist_append(headers, header.c_str());
+ headers = curl_slist_append(headers, header.c_str());
} else if (resource.priorModified) {
const std::string time =
std::string("If-Modified-Since: ") + util::rfc1123(*resource.priorModified);
- headers = curl::slist_append(headers, time.c_str());
+ headers = curl_slist_append(headers, time.c_str());
}
if (headers) {
- curl::easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
}
- handleError(curl::easy_setopt(handle, CURLOPT_PRIVATE, this));
- handleError(curl::easy_setopt(handle, CURLOPT_ERRORBUFFER, error));
- handleError(curl::easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
- handleError(curl::easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
- handleError(curl::easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
- handleError(curl::easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
- handleError(curl::easy_setopt(handle, CURLOPT_WRITEDATA, this));
- handleError(curl::easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback));
- handleError(curl::easy_setopt(handle, CURLOPT_HEADERDATA, this));
+ handleError(curl_easy_setopt(handle, CURLOPT_PRIVATE, this));
+ handleError(curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error));
+ handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
+ handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
+ handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
+ handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
+ handleError(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this));
+ handleError(curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback));
+ handleError(curl_easy_setopt(handle, CURLOPT_HEADERDATA, this));
#if LIBCURL_VERSION_NUM >= ((7) << 16 | (21) << 8 | 6) // Renamed in 7.21.6
- handleError(curl::easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"));
+ handleError(curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"));
#else
- handleError(curl::easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate"));
+ handleError(curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate"));
#endif
- handleError(curl::easy_setopt(handle, CURLOPT_USERAGENT, "MapboxGL/1.0"));
- handleError(curl::easy_setopt(handle, CURLOPT_SHARE, context->share));
+ handleError(curl_easy_setopt(handle, CURLOPT_USERAGENT, "MapboxGL/1.0"));
+ handleError(curl_easy_setopt(handle, CURLOPT_SHARE, context->share));
// Start requesting the information.
- handleError(curl::multi_add_handle(context->multi, handle));
+ handleError(curl_multi_add_handle(context->multi, handle));
}
HTTPRequest::~HTTPRequest() {
- handleError(curl::multi_remove_handle(context->multi, handle));
+ handleError(curl_multi_remove_handle(context->multi, handle));
context->returnHandle(handle);
handle = nullptr;
if (headers) {
- curl::slist_free_all(headers);
+ curl_slist_free_all(headers);
headers = nullptr;
}
}
@@ -399,7 +321,7 @@ size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const
// Always overwrite the modification date; We might already have a value here from the
// Date header, but this one is more accurate.
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->modified = Timestamp{ Seconds(curl::getdate(value.c_str(), nullptr)) };
+ baton->response->modified = Timestamp{Seconds(curl_getdate(value.c_str(), nullptr))};
} else if ((begin = headerMatches("etag: ", buffer, length)) != std::string::npos) {
baton->response->etag = std::string(buffer + begin, length - begin - 2); // remove \r\n
} else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) {
@@ -409,7 +331,7 @@ size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const
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)) };
+ baton->response->expires = Timestamp{Seconds(curl_getdate(value.c_str(), nullptr))};
} else if ((begin = headerMatches("retry-after: ", buffer, length)) != std::string::npos) {
baton->retryAfter = std::string(buffer + begin, length - begin - 2); // remove \r\n
} else if ((begin = headerMatches("x-rate-limit-reset: ", buffer, length)) != std::string::npos) {
@@ -435,18 +357,18 @@ void HTTPRequest::handleResult(CURLcode code) {
case CURLE_COULDNT_CONNECT:
case CURLE_OPERATION_TIMEDOUT:
- response->error = std::make_unique<Error>(
- Error::Reason::Connection, std::string{ curl::easy_strerror(code) } + ": " + error);
+ response->error = std::make_unique<Error>(Error::Reason::Connection,
+ std::string{curl_easy_strerror(code)} + ": " + error);
break;
default:
- response->error = std::make_unique<Error>(
- Error::Reason::Other, std::string{ curl::easy_strerror(code) } + ": " + error);
+ response->error =
+ std::make_unique<Error>(Error::Reason::Other, std::string{curl_easy_strerror(code)} + ": " + error);
break;
}
} else {
long responseCode = 0;
- curl::easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
+ curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
if (responseCode == 200) {
if (data) {
diff --git a/platform/default/src/mbgl/storage/offline_database.cpp b/platform/default/src/mbgl/storage/offline_database.cpp
index d85b560d5a..83eea7bcc4 100644
--- a/platform/default/src/mbgl/storage/offline_database.cpp
+++ b/platform/default/src/mbgl/storage/offline_database.cpp
@@ -877,27 +877,15 @@ std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region) try {
return std::current_exception();
}
-optional<std::pair<Response, uint64_t>> OfflineDatabase::getRegionResource(int64_t regionID, const Resource& resource) try {
- auto response = getInternal(resource);
-
- if (response) {
- markUsed(regionID, resource);
- }
-
- return response;
+optional<std::pair<Response, uint64_t>> OfflineDatabase::getRegionResource(const Resource& resource) try {
+ return getInternal(resource);
} catch (const mapbox::sqlite::Exception& ex) {
handleError(ex, "read region resource");
return nullopt;
}
-optional<int64_t> OfflineDatabase::hasRegionResource(int64_t regionID, const Resource& resource) try {
- auto response = hasInternal(resource);
-
- if (response) {
- markUsed(regionID, resource);
- }
-
- return response;
+optional<int64_t> OfflineDatabase::hasRegionResource(const Resource& resource) try {
+ return hasInternal(resource);
} catch (const mapbox::sqlite::Exception& ex) {
handleError(ex, "query region resource");
return nullopt;
@@ -1278,6 +1266,19 @@ bool OfflineDatabase::exceedsOfflineMapboxTileCountLimit(const Resource& resourc
&& offlineMapboxTileCountLimitExceeded();
}
+void OfflineDatabase::markUsedResources(int64_t regionID, const std::list<Resource>& resources) try {
+ if (!db) {
+ initialize();
+ }
+ mapbox::sqlite::Transaction transaction(*db);
+ for (const auto& resource : resources) {
+ markUsed(regionID, resource);
+ }
+ transaction.commit();
+} catch (const mapbox::sqlite::Exception& ex) {
+ handleError(ex, "mark resources as used");
+}
+
std::exception_ptr OfflineDatabase::resetDatabase() try {
removeExisting();
initialize();
diff --git a/platform/default/src/mbgl/storage/offline_download.cpp b/platform/default/src/mbgl/storage/offline_download.cpp
index bdcc99cc15..939164bd35 100644
--- a/platform/default/src/mbgl/storage/offline_download.cpp
+++ b/platform/default/src/mbgl/storage/offline_download.cpp
@@ -21,6 +21,13 @@
#include <set>
+namespace {
+
+const size_t kResourcesBatchSize = 64;
+const size_t kMarkBatchSize = 200;
+
+} // namespace
+
namespace mbgl {
using namespace style;
@@ -144,8 +151,9 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) {
if (urlOrTileset.is<Tileset>()) {
- result->requiredResourceCount +=
- tileCount(definition, type, tileSize, urlOrTileset.get<Tileset>().zoomRange);
+ uint64_t tileSourceCount = tileCount(definition, type, tileSize, urlOrTileset.get<Tileset>().zoomRange);
+ result->requiredTileCount += tileSourceCount;
+ result->requiredResourceCount += tileSourceCount;
} else {
result->requiredResourceCount += 1;
const auto& url = urlOrTileset.get<std::string>();
@@ -154,8 +162,9 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
style::conversion::Error error;
optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error);
if (tileset) {
- result->requiredResourceCount +=
- tileCount(definition, type, tileSize, (*tileset).zoomRange);
+ uint64_t tileSourceCount = tileCount(definition, type, tileSize, (*tileset).zoomRange);
+ result->requiredTileCount += tileSourceCount;
+ result->requiredResourceCount += tileSourceCount;
}
} else {
result->requiredResourceCountIsPrecise = false;
@@ -228,7 +237,7 @@ void OfflineDownload::activateDownload() {
styleResource.setPriority(Resource::Priority::Low);
styleResource.setUsage(Resource::Usage::Offline);
- ensureResource(styleResource, [&](Response styleResponse) {
+ ensureResource(std::move(styleResource), [&](Response styleResponse) {
status.requiredResourceCountIsPrecise = true;
style::Parser parser;
@@ -250,7 +259,7 @@ void OfflineDownload::activateDownload() {
sourceResource.setPriority(Resource::Priority::Low);
sourceResource.setUsage(Resource::Usage::Offline);
- ensureResource(sourceResource, [=](Response sourceResponse) {
+ ensureResource(std::move(sourceResource), [=](Response sourceResponse) {
style::conversion::Error error;
optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse.data, error);
if (tileset) {
@@ -353,12 +362,15 @@ void OfflineDownload::activateDownload() {
*/
void OfflineDownload::continueDownload() {
if (resourcesRemaining.empty() && status.complete()) {
+ markPendingUsedResources();
setState(OfflineRegionDownloadState::Inactive);
return;
}
+ if (resourcesToBeMarkedAsUsed.size() >= kMarkBatchSize) markPendingUsedResources();
+
while (!resourcesRemaining.empty() && requests.size() < onlineFileSource.getMaximumConcurrentRequests()) {
- ensureResource(resourcesRemaining.front());
+ ensureResource(std::move(resourcesRemaining.front()));
resourcesRemaining.pop_front();
}
}
@@ -373,12 +385,16 @@ void OfflineDownload::queueResource(Resource&& resource) {
resource.setPriority(Resource::Priority::Low);
resource.setUsage(Resource::Usage::Offline);
status.requiredResourceCount++;
+ if (resource.kind == mbgl::Resource::Kind::Tile) {
+ status.requiredTileCount++;
+ }
resourcesRemaining.push_front(std::move(resource));
}
void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tileset& tileset) {
tileCover(definition, type, tileSize, tileset.zoomRange, [&](const auto& tile) {
status.requiredResourceCount++;
+ status.requiredTileCount++;
auto tileResource = Resource::tile(
tileset.tiles[0], definition.match([](auto& def) { return def.pixelRatio; }),
@@ -391,7 +407,12 @@ void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tiles
});
}
-void OfflineDownload::ensureResource(const Resource& resource,
+void OfflineDownload::markPendingUsedResources() {
+ offlineDatabase.markUsedResources(id, resourcesToBeMarkedAsUsed);
+ resourcesToBeMarkedAsUsed.clear();
+}
+
+void OfflineDownload::ensureResource(Resource&& resource,
std::function<void(Response)> callback) {
assert(resource.priority == Resource::Priority::Low);
assert(resource.usage == Resource::Usage::Offline);
@@ -399,24 +420,29 @@ void OfflineDownload::ensureResource(const Resource& resource,
auto workRequestsIt = requests.insert(requests.begin(), nullptr);
*workRequestsIt = util::RunLoop::Get()->invokeCancellable([=]() {
requests.erase(workRequestsIt);
-
+ const auto resourceKind = resource.kind;
auto getResourceSizeInDatabase = [&] () -> optional<int64_t> {
+ optional<int64_t> result;
if (!callback) {
- return offlineDatabase.hasRegionResource(id, resource);
- }
- optional<std::pair<Response, uint64_t>> response = offlineDatabase.getRegionResource(id, resource);
- if (!response) {
- return {};
+ result = offlineDatabase.hasRegionResource(resource);
+ } else {
+ optional<std::pair<Response, uint64_t>> response = offlineDatabase.getRegionResource(resource);
+ if (response) {
+ callback(response->first);
+ result = response->second;
+ }
}
- callback(response->first);
- return response->second;
+
+ if (result) resourcesToBeMarkedAsUsed.emplace_back(std::move(resource));
+ return result;
};
optional<int64_t> offlineResponse = getResourceSizeInDatabase();
if (offlineResponse) {
+ assert(!resourcesToBeMarkedAsUsed.empty());
status.completedResourceCount++;
status.completedResourceSize += *offlineResponse;
- if (resource.kind == Resource::Kind::Tile) {
+ if (resourceKind == Resource::Kind::Tile) {
status.completedTileCount += 1;
status.completedTileSize += *offlineResponse;
}
@@ -448,7 +474,7 @@ void OfflineDownload::ensureResource(const Resource& resource,
buffer.emplace_back(resource, onlineResponse);
// Flush buffer periodically
- if (buffer.size() == 64 || resourcesRemaining.size() == 0) {
+ if (buffer.size() == kResourcesBatchSize || resourcesRemaining.empty()) {
try {
offlineDatabase.putRegionResources(id, buffer, status);
} catch (const MapboxTileLimitExceededException&) {
diff --git a/platform/default/src/mbgl/storage/sqlite3.cpp b/platform/default/src/mbgl/storage/sqlite3.cpp
index 665e23b437..522210f88f 100644
--- a/platform/default/src/mbgl/storage/sqlite3.cpp
+++ b/platform/default/src/mbgl/storage/sqlite3.cpp
@@ -125,6 +125,7 @@ mapbox::util::variant<Database, Exception> Database::tryOpen(const std::string &
const int error = sqlite3_open_v2(filename.c_str(), &db, flags | SQLITE_OPEN_URI, nullptr);
if (error != SQLITE_OK) {
const auto message = sqlite3_errmsg(db);
+ sqlite3_close(db);
return Exception { error, message };
}
return Database(std::make_unique<DatabaseImpl>(db));
diff --git a/platform/default/src/mbgl/util/format_number.cpp b/platform/default/src/mbgl/util/format_number.cpp
index 7cc863818a..d1b51e11a1 100644
--- a/platform/default/src/mbgl/util/format_number.cpp
+++ b/platform/default/src/mbgl/util/format_number.cpp
@@ -23,10 +23,14 @@ std::string formatNumber(double number, const std::string& localeId, const std::
.toString();
} else {
ustr = icu::number::NumberFormatter::with()
- .precision(icu::number::Precision::minMaxFraction(minFractionDigits, maxFractionDigits))
- .locale(locale)
- .formatDouble(number, status)
- .toString();
+#if U_ICU_VERSION_MAJOR_NUM >= 62
+ .precision(icu::number::Precision::minMaxFraction(minFractionDigits, maxFractionDigits))
+#else
+ .rounding(icu::number::Rounder::minMaxFraction(minFractionDigits, maxFractionDigits))
+#endif
+ .locale(locale)
+ .formatDouble(number, status)
+ .toString();
}
return ustr.toUTF8String(formatted);
}
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 8fe5ead445..f2394d1b84 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -5,18 +5,63 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
## master
### Styles and rendering
+* Added an `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]` method to provide the snapshot's current `CGContext` in order to perform custom drawing on `MGLMapSnapShot` objects. ([#15530](https://github.com/mapbox/mapbox-gl-native/pull/15530))
+* Fixed an issue that `maxzoom` in style `Sources` option was ignored when URL resource is provided. It may cause problems such as extra tiles downloading at higher zoom level than `maxzoom`, or problems that wrong setting of `overscaledZ` in `OverscaledTileID` that will be passed to `SymbolLayout`, leading wrong rendering appearance. ([#15581](https://github.com/mapbox/mapbox-gl-native/pull/15581))
-* Fixed an issue where it was possible to set the map’s content insets then tilt the map enough to see the horizon, causing performance issues. ([#15195](https://github.com/mapbox/mapbox-gl-native/pull/15195))
+### Performance improvements
-## 5.3.0
+* Newly loaded labels appear faster on the screen. ([#15308](https://github.com/mapbox/mapbox-gl-native/pull/15308))
+
+## 5.4.0
+
+### Styles and rendering
+
+* Fixed crashes triggered when `MGLSource` and `MGLStyleLayer` objects are accessed after having been invalidated after a style change. ([#15539](https://github.com/mapbox/mapbox-gl-native/pull/15539))
+* Fixed a rendering issue of `collisionBox` when `MGLSymbolStyleLayer.textTranslate` or `MGLSymbolStyleLayer.iconTranslate` is enabled. ([#15467](https://github.com/mapbox/mapbox-gl-native/pull/15467))
+* Fixed an issue where the scale bar text would become illegible if iOS 13 dark mode was enabled. ([#15524](https://github.com/mapbox/mapbox-gl-native/pull/15524))
+* Enabled use of `MGLSymbolStyleLayer.textOffset` option together with `MGLSymbolStyleLayer.textVariableAnchor` (if `MGLSymbolStyleLayer.textRadialOffset` option is not provided). ([#15542](https://github.com/mapbox/mapbox-gl-native/pull/15542))
+* Fixed an issue with the appearance of the compass text in iOS 13. ([#15547](https://github.com/mapbox/mapbox-gl-native/pull/15547))
+* Fixed a bug where the completion block passed to `-[MGLMapView flyToCamera:completionHandler:` (and related methods) wouldn't be called. ([#15473](https://github.com/mapbox/mapbox-gl-native/pull/15473))
+
+### User interaction
+
+* Fixed an issue that caused the tilt gesture to trigger too easily and conflict with pinch or pan gestures. ([#15349](https://github.com/mapbox/mapbox-gl-native/pull/15349))
+* Fixed an issue that caused the map to rotate too easily during a pinch gesture. [(#15562)](https://github.com/mapbox/mapbox-gl-native/pull/15562)
+
+### Performance improvements
+
+* Improved offline region download performance by batching certain database activities. ([#15521](https://github.com/mapbox/mapbox-gl-native/pull/15521))
+* Newly loaded labels appear faster on the screen. ([#15308](https://github.com/mapbox/mapbox-gl-native/pull/15308))
+
+### Other changes
+
+* Fixed a potential integer overflow at high zoom levels. ([#15560](https://github.com/mapbox/mapbox-gl-native/pull/15560))
+* Fixed a bug with annotation view positions after camera transitions. ([#15122](https://github.com/mapbox/mapbox-gl-native/pull/15122/))
+* Fixed constant repainting for the sources with invisible layers, caused by `RenderSource::hasFadingTiles()` returning `true` all the time. ([#15600](https://github.com/mapbox/mapbox-gl-native/pull/15600))
+
+## 5.3.0 - August 28, 2019
+
+This release changes how offline tile requests are billed — they are now billed on a pay-as-you-go basis and all developers are able raise the offline tile limit for their users. Offline requests were previously exempt from monthly active user (MAU) billing and increasing the offline per-user tile limit to more than 6,000 tiles required the purchase of an enterprise license. By upgrading to this release, you are opting into the changes outlined in [this blog post](https://blog.mapbox.com/offline-maps-for-all-bb0fc51827be) and [#15380](https://github.com/mapbox/mapbox-gl-native/pull/15380).
### Styles and rendering
* Fixed flickering on style change for the same tile set. ([#15127](https://github.com/mapbox/mapbox-gl-native/pull/15127))
+* Fixed performance issues when tilting a map with custom content insets. ([#15195](https://github.com/mapbox/mapbox-gl-native/pull/15195))
+* Fixed an issue where animated camera transitions zoomed in or out too dramatically. ([#15281](https://github.com/mapbox/mapbox-gl-native/pull/15281))
+* Enabled variable label placement when `MGLSymbolStyleLayer.textAllowsOverlap` is set to true. ([#15354](https://github.com/mapbox/mapbox-gl-native/pull/15354))
+* Added the `MGLSymbolStyleLayer.textWritingModes` layout property. This property can be set to `MGLTextWritingModeHorizontal` or `MGLTextWritingModeVertical`. ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932))
+* Fixed rendering and collision detection issues with using `MGLSymbolStyleLayer.textVariableAnchor` and `MGLSymbolStyleLayer.iconTextFit` properties on the same layer. ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367))
+* Fixed symbol overlap when zooming out quickly. ([#15416](https://github.com/mapbox/mapbox-gl-native/pull/15416))
+* Fixed a rendering issue where non-SDF icons would be treated as SDF icons if they are in the same layer. ([#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456))
### Other changes
-* `MGLLoggingLevel` has been updated to better match core log levels. You can now use `MGLLoggingConfiguration.loggingLevel` to filter logs from core. [#15120](https://github.com/mapbox/mapbox-gl-native/pull/15120)
+* Fixed a bug where glyphs generated through the LocalGlyphRasterizer interface were changing fonts during some zoom changes. ([#15407](https://github.com/mapbox/mapbox-gl-native/pull/15407))
+* Fixed use of objects after moving, potentially causing crashes. ([#15408](https://github.com/mapbox/mapbox-gl-native/pull/15408))
+* Fixed a possible crash that could be caused by invoking the wrong layer implementation casting function. ([#15398](https://github.com/mapbox/mapbox-gl-native/pull/15398))
+* Fixed a rare crash when tile download requests returned “404 Not Found” errors. ([#15313](https://github.com/mapbox/mapbox-gl-native/pull/15313))
+* `MGLLoggingLevel` has been updated to better match core log levels. You can now use `MGLLoggingConfiguration.loggingLevel` to filter logs from core. ([#15120](https://github.com/mapbox/mapbox-gl-native/pull/15120))
+* Fixed an issue where the scale bar could show `0 mm` instead of `0`. ([#15381](https://github.com/mapbox/mapbox-gl-native/pull/15381))
## 4.11.2 - July 30, 2019
diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md
index eee75b9993..1d0d7dc647 100644
--- a/platform/ios/INSTALL.md
+++ b/platform/ios/INSTALL.md
@@ -108,7 +108,7 @@ For instructions on installing stable release versions of the Mapbox Maps SDK fo
##### Testing pre-releases with CocoaPods
-To test pre-releases of the dynamic framework, directly specify the version in your Podfile:
+To test pre-releases of the dynamic framework, directly specify the version in your `Podfile`:
```rb
pod 'Mapbox-iOS-SDK', '~> x.x.x-alpha.1'
@@ -126,10 +126,20 @@ pod 'Mapbox-iOS-SDK-snapshot-dynamic', podspec: 'https://raw.githubusercontent.c
1. Build from source manually, per above.
-1. Update your app’s `Podfile` to point to `Mapbox-iOS-SDK.podspec`.
+1. Edit [`Mapbox-iOS-SDK.podspec`](./Mapbox-iOS-SDK.podspec) to point to the local build output:
+
+ ```diff
+ m.source = {
+ - :http => "https://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/mapbox-ios-sdk-#{m.version.to_s}-dynamic.zip",
+ + :path => "../../build/ios/pkg/dynamic/",
+ :flatten => true
+ }
+ ```
+
+1. Update your app’s `Podfile` to point to the edited `Mapbox-iOS-SDK.podspec`.
```rb
- pod 'Mapbox-iOS-SDK', :path => '{...}/build/ios/pkg/{dynamic|static}/Mapbox-iOS-SDK.podspec'
+ pod 'Mapbox-iOS-SDK', :path => '{...}/platform/ios/Mapbox-iOS-SDK.podspec'
```
1. Run `pod update` to grab the newly-built library.
@@ -138,10 +148,12 @@ If using the static framework, add `$(inherited)` to your target’s Other Linke
##### Using pre-stripped releases with CocoaPods
-If you choose to commit the contents of your `Pods` directory to source control and are encountering file size limitations, you may wish to use builds that have been pre-stripped of debug symbols.
+If you choose to commit the contents of your `Pods` directory to source control and are encountering file size limitations, you may wish to use builds that have been pre-stripped of debug symbols. We publish these to [our public podspecs repository](https://github.com/mapbox/pod-specs/), which should be added as an additional `source` in your app’s `Podfile`.
```rb
-pod 'Mapbox-iOS-SDK-stripped', podspec: 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/ios-v{x.x.x}/platform/ios/Mapbox-iOS-SDK-stripped.podspec'
+source 'https://github.com/mapbox/pod-specs.git'
+
+pod 'Mapbox-iOS-SDK-stripped', '~> x.x.x'
```
Note that these builds lack some debugging information, which could make development more difficult and result in less useful crash reports. Though initially smaller on disk, using these builds has no effect on the ultimate size of your application — see our [Understanding iOS Framework Size guide](https://www.mapbox.com/help/ios-framework-size/) for more information.
diff --git a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm
index f129277e87..1b3603419e 100644
--- a/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.m
+++ b/platform/ios/Integration Tests/Annotation Tests/MGLAnnotationViewIntegrationTests.mm
@@ -4,6 +4,13 @@
#import "MGLTestLocationManager.h"
#import "MGLCompactCalloutView.h"
+#import "MGLGeometry_Private.h"
+#import "MGLMapView_Private.h"
+
+#include <mbgl/util/geo.hpp>
+#include <mbgl/map/camera.hpp>
+#include <mbgl/map/map.hpp>
+
@interface MGLTestCalloutView : MGLCompactCalloutView
@property (nonatomic) BOOL implementsMarginHints;
@end
@@ -21,6 +28,10 @@
@interface MGLMapView (Tests)
- (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)persist;
+- (id <MGLAnnotation>)annotationWithTag:(MGLAnnotationTag)tag;
+- (MGLMapCamera *)cameraByRotatingToDirection:(CLLocationDirection)degrees aroundAnchorPoint:(CGPoint)anchorPoint;
+- (MGLMapCamera *)cameraByZoomingToZoomLevel:(double)zoom aroundAnchorPoint:(CGPoint)anchorPoint;
+- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions;
@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
@end
@@ -502,6 +513,267 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
XCTAssertEqual(originalFrame.origin.y + offset.y, offsetFrame.origin.y);
}
+#pragma mark - Rotating/zooming
+
+- (void)testSelectingAnnotationWhenMapIsRotated {
+
+ CLLocationCoordinate2D coordinates[] = {
+ { 40.0, 40.0 },
+ { NAN, NAN }
+ };
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+ MGLPointAnnotation *annotation = annotations.firstObject;
+
+ // Rotate
+ CLLocationDirection lastAngle = 0.0;
+
+ srand48(0);
+ for (NSInteger iter = 0; iter < 10; iter++ ) {
+
+ CLLocationDirection angle = (CLLocationDirection)((drand48()*1080.0) - 540.0);
+
+ CGPoint anchor = CGPointMake(drand48()*CGRectGetWidth(self.mapView.bounds), drand48()*CGRectGetHeight(self.mapView.bounds));
+
+ NSString *activityTitle = [NSString stringWithFormat:@"Rotate to: %0.1f from: %0.1f", angle, lastAngle];
+ [XCTContext runActivityNamed:activityTitle
+ block:^(id<XCTActivity> _Nonnull activity) {
+
+ MGLMapCamera *toCamera = [self.mapView cameraByRotatingToDirection:angle aroundAnchorPoint:anchor];
+ [self internalTestSelecting:annotation withCamera:toCamera];
+ }];
+
+ lastAngle = angle;
+ }
+}
+
+- (void)testSelectingAnnotationWhenMapIsScaled {
+
+ CLLocationCoordinate2D coordinates[] = {
+ { 0.005, 0.005 },
+ { NAN, NAN }
+ };
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+ MGLPointAnnotation *annotation = annotations.firstObject;
+
+ CGPoint anchor = CGPointMake(CGRectGetMidX(self.mapView.bounds), CGRectGetMidY(self.mapView.bounds));
+
+ srand48(0);
+ for (NSInteger iter = 0; iter < 10; iter++ ) {
+
+ double zoom = (double)(drand48()*14.0);
+
+ NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
+ [XCTContext runActivityNamed:activityTitle
+ block:^(id<XCTActivity> _Nonnull activity) {
+ MGLMapCamera *toCamera = [self.mapView cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:anchor];
+ [self internalTestSelecting:annotation withCamera:toCamera];
+ }];
+ }
+}
+
+- (void)testSelectingAnnotationWhenMapIsScaledAndRotated {
+
+ CLLocationCoordinate2D coordinates[] = {
+ { 0.005, 0.005 },
+ { NAN, NAN }
+ };
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+ MGLPointAnnotation *annotation = annotations.firstObject;
+
+ srand48(0);
+ for (NSInteger iter = 0; iter < 10; iter++ ) {
+
+ double zoom = (double)(7.0 + drand48()*7.0);
+ CLLocationDirection angle = (CLLocationDirection)((drand48()*1080.0) - 540.0);
+
+ CGPoint anchor = CGPointMake(drand48()*CGRectGetWidth(self.mapView.bounds), drand48()*CGRectGetHeight(self.mapView.bounds));
+
+ NSString *activityTitle = [NSString stringWithFormat:@"Zoom to %0.1f", zoom];
+ [XCTContext runActivityNamed:activityTitle
+ block:^(id<XCTActivity> _Nonnull activity)
+ {
+ mbgl::CameraOptions currentCameraOptions;
+
+ currentCameraOptions.bearing = angle;
+ currentCameraOptions.anchor = mbgl::ScreenCoordinate { anchor.x, anchor.y };
+ currentCameraOptions.zoom = zoom;
+ MGLMapCamera *toCamera = [self.mapView cameraForCameraOptions:currentCameraOptions];
+
+ [self internalTestSelecting:annotation withCamera:toCamera];
+ }];
+ }
+}
+
+
+- (void)testShowingAnnotationsThenSelectingAnimated {
+ [self internalTestShowingAnnotationsThenSelectingAnimated:YES];
+}
+
+- (void)testShowingAnnotationsThenSelecting {
+ [self internalTestShowingAnnotationsThenSelectingAnimated:NO];
+}
+
+- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated {
+ srand48(0);
+
+ CGFloat maxXPadding = std::max(CGRectGetWidth(self.mapView.bounds)/5.0, 100.0);
+ CGFloat maxYPadding = std::max(CGRectGetHeight(self.mapView.bounds)/5.0, 100.0);
+
+ for (int i = 0; i < 10; i++) {
+ UIEdgeInsets edgePadding;
+ edgePadding.top = floor(drand48()*maxYPadding);
+ edgePadding.bottom = floor(drand48()*maxYPadding);
+ edgePadding.left = floor(drand48()*maxXPadding);
+ edgePadding.right = floor(drand48()*maxXPadding);
+
+ UIEdgeInsets contentInsets;
+ contentInsets.top = floor(drand48()*maxYPadding);
+ contentInsets.bottom = floor(drand48()*maxYPadding);
+ contentInsets.left = floor(drand48()*maxXPadding);
+ contentInsets.right = floor(drand48()*maxXPadding);
+
+ [self internalTestShowingAnnotationsThenSelectingAnimated:animated edgePadding:edgePadding contentInsets:contentInsets];
+ }
+}
+
+- (void)internalTestShowingAnnotationsThenSelectingAnimated:(BOOL)animated edgePadding:(UIEdgeInsets)edgeInsets contentInsets:(UIEdgeInsets)contentInsets {
+ CLLocationCoordinate2D coordinates[21];
+
+ for (int i = 0; i < (int)(sizeof(coordinates)/sizeof(coordinates[0])); i++)
+ {
+ coordinates[i].latitude = drand48();
+ coordinates[i].longitude = drand48();
+ }
+ coordinates[20] = CLLocationCoordinate2DMake(NAN, NAN);
+
+ NSArray *annotations = [self internalAddAnnotationsAtCoordinates:coordinates];
+
+ XCTestExpectation *showCompleted = [self expectationWithDescription:@"showCompleted"];
+
+ self.mapView.contentInset = contentInsets;
+ [self.mapView showAnnotations:annotations
+ edgePadding:edgeInsets
+ animated:animated
+ completionHandler:^{
+ [showCompleted fulfill];
+ }];
+
+ [self waitForExpectations:@[showCompleted] timeout:3.5];
+
+ // These tests will fail if this isn't here. But this isn't quite what we're
+ // seeing in https://github.com/mapbox/mapbox-gl-native/issues/15106
+ [self waitForCollisionDetectionToRun];
+
+ for (MGLPointAnnotation *point in annotations) {
+ [self internalSelectDeselectAnnotation:point];
+ }
+
+ [self.mapView removeAnnotations:annotations];
+ self.mapView.contentInset = UIEdgeInsetsZero;
+ [self waitForCollisionDetectionToRun];
+}
+
+- (NSArray*)internalAddAnnotationsAtCoordinates:(CLLocationCoordinate2D*)coordinates
+{
+ __block NSMutableArray *annotations = [NSMutableArray array];
+
+ [XCTContext runActivityNamed:@"Map setup"
+ block:^(id<XCTActivity> _Nonnull activity)
+ {
+
+ NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReuseIdentifer";
+
+ CGSize annotationSize = CGSizeMake(40.0, 40.0);
+
+ self.viewForAnnotation = ^MGLAnnotationView*(MGLMapView *view, id<MGLAnnotation> annotation2) {
+
+ if (![annotation2 isKindOfClass:[MGLPointAnnotation class]]) {
+ return nil;
+ }
+
+ // No dequeue
+ MGLAnnotationView *annotationView = [[MGLAnnotationView alloc] initWithAnnotation:annotation2 reuseIdentifier:MGLTestAnnotationReuseIdentifer];
+ annotationView.bounds = (CGRect){ .origin = CGPointZero, .size = annotationSize };
+ annotationView.backgroundColor = UIColor.redColor;
+ annotationView.enabled = YES;
+
+ return annotationView;
+ };
+
+ CLLocationCoordinate2D *coordinatePtr = coordinates;
+ while (!isnan(coordinatePtr->latitude)) {
+ CLLocationCoordinate2D coordinate = *coordinatePtr++;
+
+ MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
+ annotation.title = NSStringFromSelector(_cmd);
+ annotation.coordinate = coordinate;
+ [annotations addObject:annotation];
+ }
+
+ [self.mapView addAnnotations:annotations];
+
+ }];
+
+ NSArray *copiedAnnotations = [annotations copy];
+ annotations = nil;
+
+ return copiedAnnotations;
+}
+
+- (void)internalTestSelecting:(MGLPointAnnotation*)point withCamera:(MGLMapCamera*)camera {
+
+ // Rotate
+ XCTestExpectation *rotationCompleted = [self expectationWithDescription:@"rotationCompleted"];
+ [self.mapView setCamera:camera withDuration:0.1 animationTimingFunction:nil completionHandler:^{
+ [rotationCompleted fulfill];
+ }];
+
+ [self waitForExpectations:@[rotationCompleted] timeout:1.5];
+
+ // Collision detection may not have completed, if not we may not get our annotation.
+ [self waitForCollisionDetectionToRun];
+
+ // Look up annotation at point
+ [self internalSelectDeselectAnnotation:point];
+}
+
+- (void)internalSelectDeselectAnnotation:(MGLPointAnnotation*)point {
+ [XCTContext runActivityNamed:[NSString stringWithFormat:@"Select annotation: %@", point]
+ block:^(id<XCTActivity> _Nonnull activity)
+ {
+ CGPoint annotationPoint = [self.mapView convertCoordinate:point.coordinate toPointToView:self.mapView];
+
+ MGLAnnotationTag tagAtPoint = [self.mapView annotationTagAtPoint:annotationPoint persistingResults:YES];
+ if (tagAtPoint != UINT32_MAX)
+ {
+ id <MGLAnnotation> annotation = [self.mapView annotationWithTag:tagAtPoint];
+ XCTAssertNotNil(annotation);
+
+ // Select
+ XCTestExpectation *selectionCompleted = [self expectationWithDescription:@"Selection completed"];
+ [self.mapView selectAnnotation:annotation moveIntoView:NO animateSelection:NO completionHandler:^{
+ [selectionCompleted fulfill];
+ }];
+
+ [self waitForExpectations:@[selectionCompleted] timeout:0.05];
+
+ XCTAssert(self.mapView.selectedAnnotations.count == 1, @"There should only be 1 selected annotation");
+ XCTAssertEqualObjects(self.mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected");
+
+ // Deselect
+ [self.mapView deselectAnnotation:annotation animated:NO];
+ }
+ else
+ {
+ XCTFail(@"Should be an annotation at this point: %@", NSStringFromCGPoint(annotationPoint));
+ }
+ }];
+
+}
+
#pragma mark - Utilities
- (void)runRunLoop {
@@ -529,6 +801,7 @@ static const CGPoint kAnnotationRelativeScale = { 0.05f, 0.125f };
- (void)waitForCollisionDetectionToRun {
XCTAssertNil(self.renderFinishedExpectation, @"Incorrect test setup");
+ [self.mapView setNeedsRerender];
self.renderFinishedExpectation = [self expectationWithDescription:@"Map view should be rendered"];
XCTestExpectation *timerExpired = [self expectationWithDescription:@"Timer expires"];
diff --git a/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm
new file mode 100644
index 0000000000..1527e8dbe5
--- /dev/null
+++ b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionFinishTests.mm
@@ -0,0 +1,109 @@
+#import "MGLMapViewIntegrationTest.h"
+#import "MGLTestUtility.h"
+#import "../../darwin/src/MGLGeometry_Private.h"
+
+#include <mbgl/map/camera.hpp>
+
+@interface MGLCameraTransitionFinishTests : MGLMapViewIntegrationTest
+@end
+
+@implementation MGLCameraTransitionFinishTests
+
+- (void)testEaseToCompletionHandler {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView setCamera:camera
+ withDuration:0.0
+ animationTimingFunction:nil
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+
+- (void)testEaseToCompletionHandlerAnimated {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView setCamera:camera
+ withDuration:0.3
+ animationTimingFunction:nil
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+
+- (void)testFlyToCompletionHandler {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView flyToCamera:camera
+ withDuration:0.0
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+
+- (void)testFlyToCompletionHandlerAnimated {
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0.0, 0.0),
+ CLLocationCoordinate2DMake(1.0, 1.0));
+ MGLMapCamera *camera = [self.mapView cameraThatFitsCoordinateBounds:bounds];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"Completion block should be called"];
+
+ [self.mapView flyToCamera:camera
+ withDuration:0.3
+ completionHandler:^{
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+}
+@end
+
+#pragma mark - camera transitions with NaN values
+
+@interface MGLMapView (MGLCameraTransitionFinishNaNTests)
+- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets;
+@end
+
+@interface MGLCameraTransitionNaNZoomMapView: MGLMapView
+@end
+
+@implementation MGLCameraTransitionNaNZoomMapView
+- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets {
+ mbgl::CameraOptions options = [super cameraOptionsObjectForAnimatingToCamera:camera edgePadding:insets];
+ options.zoom = NAN;
+ return options;
+}
+@end
+
+// Subclass the entire test suite, but with a different MGLMapView subclass
+@interface MGLCameraTransitionFinishNaNTests : MGLCameraTransitionFinishTests
+@end
+
+@implementation MGLCameraTransitionFinishNaNTests
+- (MGLMapView *)mapViewForTestWithFrame:(CGRect)rect styleURL:(NSURL *)styleURL {
+ return [[MGLCameraTransitionNaNZoomMapView alloc] initWithFrame:rect styleURL:styleURL];
+}
+@end
+
diff --git a/platform/ios/Integration Tests/MGLCameraTransitionTests.mm b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionTests.mm
index 9679c4c11f..27ab7964c1 100644
--- a/platform/ios/Integration Tests/MGLCameraTransitionTests.mm
+++ b/platform/ios/Integration Tests/Camera Tests/MGLCameraTransitionTests.mm
@@ -2,10 +2,10 @@
#import "MGLTestUtility.h"
#import "../../darwin/src/MGLGeometry_Private.h"
-@interface MBCameraTransitionTests : MGLMapViewIntegrationTest
+@interface MGLCameraTransitionTests : MGLMapViewIntegrationTest
@end
-@implementation MBCameraTransitionTests
+@implementation MGLCameraTransitionTests
- (void)testSetAndResetNorthWithDispatchAsyncInDelegateMethod {
@@ -17,7 +17,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -48,7 +48,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -79,7 +79,7 @@
__block BOOL finishedReset = NO;
self.regionIsChanging = ^(MGLMapView *mapView) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
if (!startedReset) {
@@ -91,7 +91,7 @@
};
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
MGLTestAssert(strongSelf, startedReset);
@@ -127,7 +127,7 @@
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
[strongSelf.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO];
MGLTestAssertEqualWithAccuracy(strongSelf,
@@ -160,7 +160,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -248,7 +248,7 @@
self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) {
- MBCameraTransitionTests *strongSelf = weakself;
+ MGLCameraTransitionTests *strongSelf = weakself;
if (!strongSelf) return;
@@ -333,7 +333,7 @@
#pragma mark - Pending tests
-- (void)testContinuallyResettingNorthInIsChangingPENDING {
+- (void)testContinuallyResettingNorthInIsChanging🙁{
// See https://github.com/mapbox/mapbox-gl-native/pull/11614
// This test currently fails, unsurprisingly, since we're continually
// setting the camera to the same parameters during its update.
@@ -365,8 +365,8 @@
XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction);
}
-- (void)testContinuallySettingCoordinateInIsChangingPENDING {
- // See above comment in `-testContinuallyResettingNorthInIsChangingPENDING`
+- (void)testContinuallySettingCoordinateInIsChanging🙁 {
+ // See above comment in `-testContinuallyResettingNorthInIsChanging🙁`
// Reset to non-zero, prior to testing
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(0.0, 0.0) animated:NO];
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
index dedafdb83a..08576e884a 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
@@ -26,6 +26,7 @@
@interface MGLMapViewIntegrationTest : XCTestCase <MGLMapViewDelegate>
@property (nonatomic) MGLMapView *mapView;
+@property (nonatomic) UIWindow *window;
@property (nonatomic) MGLStyle *style;
@property (nonatomic) XCTestExpectation *styleLoadingExpectation;
@property (nonatomic) XCTestExpectation *renderFinishedExpectation;
@@ -38,7 +39,7 @@
@property (nonatomic) id<MGLCalloutView> (^mapViewCalloutViewForAnnotation)(MGLMapView *mapView, id<MGLAnnotation> annotation);
// Utility methods
-- (NSString*)validAccessToken;
- (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout;
- (void)waitForMapViewToBeRenderedWithTimeout:(NSTimeInterval)timeout;
+- (MGLMapView *)mapViewForTestWithFrame:(CGRect)rect styleURL:(NSURL *)styleURL;
@end
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
index 2c89bb1c77..4095b4620b 100644
--- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
@@ -2,50 +2,76 @@
@interface MGLMapView (MGLMapViewIntegrationTest)
- (void)updateFromDisplayLink:(CADisplayLink *)displayLink;
+- (void)setNeedsRerender;
@end
@implementation MGLMapViewIntegrationTest
-- (void)invokeTest {
- NSString *selector = NSStringFromSelector(self.invocation.selector);
- BOOL isPendingTest = [selector hasSuffix:@"PENDING"];
++ (XCTestSuite*)defaultTestSuite {
- if (isPendingTest) {
- NSString *runPendingTests = [[NSProcessInfo processInfo] environment][@"MAPBOX_RUN_PENDING_TESTS"];
- if (![runPendingTests boolValue]) {
- printf("warning: '%s' is a pending test - skipping\n", selector.UTF8String);
- return;
+ XCTestSuite *defaultTestSuite = [super defaultTestSuite];
+
+ NSArray *tests = defaultTestSuite.tests;
+
+ XCTestSuite *newTestSuite = [XCTestSuite testSuiteWithName:defaultTestSuite.name];
+
+ BOOL runPendingTests = [[[NSProcessInfo processInfo] environment][@"MAPBOX_RUN_PENDING_TESTS"] boolValue];
+ NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
+
+ for (XCTest *test in tests) {
+
+ // Check for pending tests
+ if ([test.name containsString:@"PENDING"] ||
+ [test.name containsString:@"🙁"]) {
+ if (!runPendingTests) {
+ printf("warning: '%s' is a pending test - skipping\n", test.name.UTF8String);
+ continue;
+ }
+ }
+
+ // Check for tests that require a valid access token
+ if ([test.name containsString:@"🔒"]) {
+ if (!accessToken) {
+ printf("warning: MAPBOX_ACCESS_TOKEN env var is required for test '%s' - skipping.\n", test.name.UTF8String);
+ continue;
+ }
}
- }
-
- [super invokeTest];
-}
-- (NSString*)validAccessToken {
- NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
- if (!accessToken) {
- printf("warning: MAPBOX_ACCESS_TOKEN env var is required for this test - skipping.\n");
- return nil;
+ [newTestSuite addTest:test];
}
+
+ return newTestSuite;
+}
- [MGLAccountManager setAccessToken:accessToken];
- return accessToken;
+- (MGLMapView *)mapViewForTestWithFrame:(CGRect)rect styleURL:(NSURL *)styleURL {
+ return [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
}
- (void)setUp {
[super setUp];
- [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
+ NSString *accessToken;
+
+ if ([self.name containsString:@"🔒"]) {
+ accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
+
+ if (!accessToken) {
+ printf("warning: MAPBOX_ACCESS_TOKEN env var is required for test '%s' - trying anyway.\n", self.name.UTF8String);
+ }
+ }
+
+ [MGLAccountManager setAccessToken:accessToken ?: @"pk.feedcafedeadbeefbadebede"];
+
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
- self.mapView = [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
+ self.mapView = [self mapViewForTestWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
self.mapView.delegate = self;
UIView *superView = [[UIView alloc] initWithFrame:UIScreen.mainScreen.bounds];
[superView addSubview:self.mapView];
- UIWindow *window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
- [window addSubview:superView];
- [window makeKeyAndVisible];
+ self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
+ [self.window addSubview:superView];
+ [self.window makeKeyAndVisible];
if (!self.mapView.style) {
[self waitForMapViewToFinishLoadingStyleWithTimeout:10];
@@ -57,6 +83,7 @@
self.renderFinishedExpectation = nil;
self.mapView = nil;
self.style = nil;
+ self.window = nil;
[MGLAccountManager setAccessToken:nil];
[super tearDown];
@@ -129,13 +156,15 @@
XCTAssertNil(self.styleLoadingExpectation);
self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
[self waitForExpectations:@[self.styleLoadingExpectation] timeout:timeout];
+ self.styleLoadingExpectation = nil;
}
- (void)waitForMapViewToBeRenderedWithTimeout:(NSTimeInterval)timeout {
XCTAssertNil(self.renderFinishedExpectation);
- [self.mapView setNeedsDisplay];
+ [self.mapView setNeedsRerender];
self.renderFinishedExpectation = [self expectationWithDescription:@"Map view should be rendered"];
[self waitForExpectations:@[self.renderFinishedExpectation] timeout:timeout];
+ self.renderFinishedExpectation = nil;
}
- (void)waitForExpectations:(NSArray<XCTestExpectation *> *)expectations timeout:(NSTimeInterval)seconds {
diff --git a/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m b/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m
new file mode 100644
index 0000000000..c7925d7896
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLMapViewPendingBlockTests.m
@@ -0,0 +1,366 @@
+#import "MGLMapViewIntegrationTest.h"
+#import "MGLTestUtility.h"
+
+@interface MGLMapView (MGLMapViewPendingBlockTests)
+@property (nonatomic) NSMutableArray *pendingCompletionBlocks;
+- (void)pauseRendering:(__unused NSNotification *)notification;
+@end
+
+@interface MGLMapViewPendingBlockTests : MGLMapViewIntegrationTest
+@property (nonatomic, copy) void (^observation)(NSDictionary*);
+@property (nonatomic) BOOL completionHandlerCalled;
+@end
+
+@implementation MGLMapViewPendingBlockTests
+
+- (void)testSetCenterCoordinate {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetCenterCoordinateAnimated {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:YES
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetVisibleCoordinateBounds {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ [strongSelf.mapView setVisibleCoordinateBounds:unitBounds
+ edgePadding:UIEdgeInsetsZero
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetVisibleCoordinateBoundsAnimated {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ [strongSelf.mapView setVisibleCoordinateBounds:unitBounds
+ edgePadding:UIEdgeInsetsZero
+ animated:YES
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetCamera {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView setCamera:camera withDuration:0.0 animationTimingFunction:nil completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testSetCameraAnimated {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView setCamera:camera withDuration:0.3 animationTimingFunction:nil completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testFlyToCamera {
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView flyToCamera:camera withDuration:0.0 completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+- (void)testFlyToCameraAnimated {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ MGLCoordinateBounds unitBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 1));
+ MGLMapCamera *camera = [strongSelf.mapView cameraThatFitsCoordinateBounds:unitBounds];
+
+ [strongSelf.mapView flyToCamera:camera withDuration:0.3 completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:nil];
+}
+
+
+#pragma mark - test interrupting regular rendering
+
+- (void)testSetCenterCoordinateSetHidden {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ dispatch_block_t addedToPending = ^{
+ __typeof__(self) strongSelf = weakSelf;
+
+ MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
+
+ // Now hide the mapview
+ strongSelf.mapView.hidden = YES;
+
+ MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:addedToPending];
+}
+
+- (void)testSetCenterCoordinatePauseRendering {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ dispatch_block_t addedToPending = ^{
+ __typeof__(self) strongSelf = weakSelf;
+
+ MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
+
+ // Pause rendering, stopping display link
+ [strongSelf.mapView pauseRendering:nil];
+
+ MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:addedToPending];
+}
+
+- (void)testSetCenterCoordinateRemoveFromSuperview {
+
+ __typeof__(self) weakSelf = self;
+
+ void (^transition)(dispatch_block_t) = ^(dispatch_block_t completion) {
+ __typeof__(self) strongSelf = weakSelf;
+
+ if (strongSelf) {
+ [strongSelf.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)
+ zoomLevel:10.0
+ direction:0
+ animated:NO
+ completionHandler:completion];
+ }
+ else {
+ completion();
+ }
+ };
+
+ dispatch_block_t addedToPending = ^{
+ __typeof__(self) strongSelf = weakSelf;
+
+ MGLTestAssert(strongSelf, !strongSelf.completionHandlerCalled);
+
+ // Remove from window, triggering validateDisplayLink
+ [strongSelf.mapView removeFromSuperview];
+
+ MGLTestAssert(strongSelf, strongSelf.completionHandlerCalled);
+ };
+
+ [self internalTestCompletionBlockAddedToPendingForTestName:NSStringFromSelector(_cmd)
+ transition:transition
+ addToPendingCallback:addedToPending];
+}
+
+#pragma mark - Shared utility methods
+
+- (void)internalTestCompletionBlockAddedToPendingForTestName:(NSString *)testName
+ transition:(void (^)(dispatch_block_t))transition
+ addToPendingCallback:(dispatch_block_t)addToPendingCallback {
+
+ XCTestExpectation *expectation = [self expectationWithDescription:testName];
+
+ __weak __typeof__(self) myself = self;
+
+ dispatch_block_t block = ^{
+ myself.completionHandlerCalled = YES;
+ [expectation fulfill];
+ };
+
+ XCTAssertNotNil(transition);
+ transition(block);
+
+ XCTAssert(!self.completionHandlerCalled);
+ XCTAssert(self.mapView.pendingCompletionBlocks.count == 0);
+
+ __block BOOL blockAddedToPendingBlocks = NO;
+
+ // Observes changes to pendingCompletionBlocks (including additions)
+ self.observation = ^(NSDictionary *change){
+
+ NSLog(@"change = %@ count = %lu", change, myself.mapView.pendingCompletionBlocks.count);
+
+ NSArray *value = change[NSKeyValueChangeNewKey];
+
+ MGLTestAssert(myself, [value isKindOfClass:[NSArray class]]);
+
+ if (value.count > 0) {
+ MGLTestAssert(myself, [value containsObject:block]);
+ MGLTestAssert(myself, !blockAddedToPendingBlocks);
+ if ([myself.mapView.pendingCompletionBlocks containsObject:block]) {
+ blockAddedToPendingBlocks = YES;
+
+ if (addToPendingCallback) {
+ addToPendingCallback();
+ }
+ }
+ }
+ };
+
+ [self.mapView addObserver:self forKeyPath:@"pendingCompletionBlocks" options:NSKeyValueObservingOptionNew context:_cmd];
+
+ [self waitForExpectations:@[expectation] timeout:0.5];
+
+ XCTAssert(blockAddedToPendingBlocks);
+ XCTAssert(self.completionHandlerCalled);
+ XCTAssert(self.mapView.pendingCompletionBlocks.count == 0);
+
+ [self.mapView removeObserver:self forKeyPath:@"pendingCompletionBlocks" context:_cmd];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
+ if (self.observation) {
+ self.observation(change);
+ }
+}
+@end
diff --git a/platform/ios/Integration Tests/MGLSourceTests.swift b/platform/ios/Integration Tests/MGLSourceTests.swift
new file mode 100644
index 0000000000..69fa0182b5
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLSourceTests.swift
@@ -0,0 +1,45 @@
+import XCTest
+
+class MGLSourceTests: MGLMapViewIntegrationTest {
+
+ // See testForRaisingExceptionsOnStaleStyleObjects for Obj-C sibling.
+ func testForRaisingExceptionsOnStaleStyleObjectsOnRemoveFromMapView() {
+
+ guard
+ let configURL = URL(string: "mapbox://examples.2uf7qges") else {
+ XCTFail()
+ return
+ }
+
+ let source = MGLVectorTileSource(identifier: "trees", configurationURL: configURL)
+ mapView.style?.addSource(source)
+
+ let bundle = Bundle(for: type(of: self))
+
+ guard let styleURL = bundle.url(forResource: "one-liner", withExtension: "json") else {
+ XCTFail()
+ return
+ }
+
+ styleLoadingExpectation = nil;
+
+ mapView.centerCoordinate = CLLocationCoordinate2D(latitude : 38.897, longitude : -77.039)
+ mapView.zoomLevel = 10.5
+ mapView.styleURL = styleURL
+
+ waitForMapViewToFinishLoadingStyle(withTimeout: 10.0)
+
+ let expect = expectation(description: "Remove source should error")
+
+ do {
+ try mapView.style?.removeSource(source, error: ())
+ }
+ catch let error as NSError {
+ XCTAssertEqual(error.domain, MGLErrorDomain)
+ XCTAssertEqual(error.code, MGLErrorCode.sourceCannotBeRemovedFromStyle.rawValue)
+ expect.fulfill()
+ }
+
+ wait(for: [expect], timeout: 0.1)
+ }
+}
diff --git a/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m
index 4501294f72..c018c457b9 100644
--- a/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m
+++ b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m
@@ -58,4 +58,55 @@
[self waitForMapViewToBeRenderedWithTimeout:10];
}
+- (void)testForRaisingExceptionsOnStaleStyleObjects {
+ self.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.897,-77.039);
+ self.mapView.zoomLevel = 10.5;
+
+ MGLVectorTileSource *source = [[MGLVectorTileSource alloc] initWithIdentifier:@"trees" configurationURL:[NSURL URLWithString:@"mapbox://examples.2uf7qges"]];
+ [self.mapView.style addSource:source];
+
+ self.styleLoadingExpectation = nil;
+ [self.mapView setStyleURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"]];
+ [self waitForMapViewToFinishLoadingStyleWithTimeout:10];
+
+ XCTAssertNotNil(source.description);
+ XCTAssertThrowsSpecificNamed(source.configurationURL, NSException, MGLInvalidStyleSourceException, @"MGLSource should raise an exception if its core peer got invalidated");
+}
+
+- (void)testForRaisingExceptionsOnStaleLayerObject {
+ self.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.897,-77.039);
+ self.mapView.zoomLevel = 10.5;
+
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ // Testing generated layers
+ MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"lineLayerID" source:source];
+ MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"rasterLayerID" source:source];
+
+ [self.mapView.style addSource:source];
+ [self.mapView.style addLayer:lineLayer];
+ [self.mapView.style addLayer:rasterLayer];
+
+ XCTAssertNoThrow(lineLayer.isVisible);
+ XCTAssertNoThrow(rasterLayer.isVisible);
+
+ XCTAssert(![source.description containsString:@"<unknown>"]);
+ XCTAssert(![lineLayer.description containsString:@"<unknown>"]);
+ XCTAssert(![rasterLayer.description containsString:@"<unknown>"]);
+
+ self.styleLoadingExpectation = nil;
+ [self.mapView setStyleURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"]];
+ [self waitForMapViewToFinishLoadingStyleWithTimeout:10];
+
+ XCTAssert([source.description containsString:@"<unknown>"]);
+ XCTAssert([lineLayer.description containsString:@"<unknown>"]);
+ XCTAssert([rasterLayer.description containsString:@"<unknown>"]);
+
+ XCTAssertThrowsSpecificNamed(lineLayer.isVisible, NSException, MGLInvalidStyleLayerException, @"Layer should raise an exception if its core peer got invalidated");
+ XCTAssertThrowsSpecificNamed(rasterLayer.isVisible, NSException, MGLInvalidStyleLayerException, @"Layer should raise an exception if its core peer got invalidated");
+
+ XCTAssertThrowsSpecificNamed([self.mapView.style removeLayer:lineLayer], NSException, NSInvalidArgumentException, @"Style should raise an exception when attempting to remove an invalid layer (e.g. if its core peer got invalidated)");
+ XCTAssertThrowsSpecificNamed([self.mapView.style removeLayer:rasterLayer], NSException, NSInvalidArgumentException, @"Style should raise an exception when attempting to remove an invalid layer (e.g. if its core peer got invalidated)");
+}
@end
diff --git a/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m b/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m
index f9217bae5f..22de4c6aa5 100644
--- a/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m
+++ b/platform/ios/Integration Tests/MGLStyleURLIntegrationTest.m
@@ -6,36 +6,32 @@
@implementation MGLStyleURLIntegrationTest
- (void)internalTestWithStyleSelector:(SEL)selector {
- if (![self validAccessToken]) {
- return;
- }
-
self.mapView.styleURL = [MGLStyle performSelector:selector];
[self waitForMapViewToFinishLoadingStyleWithTimeout:5];
}
-- (void)testLoadingStreetsStyleURL {
+- (void)testLoadingStreetsStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(streetsStyleURL)];
}
-- (void)testLoadingOutdoorsStyleURL {
+- (void)testLoadingOutdoorsStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(outdoorsStyleURL)];
}
-- (void)testLoadingLightStyleURL {
+- (void)testLoadingLightStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(lightStyleURL)];
}
-- (void)testLoadingDarkStyleURL {
+- (void)testLoadingDarkStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(darkStyleURL)];
}
-- (void)testLoadingSatelliteStyleURL {
+- (void)testLoadingSatelliteStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(satelliteStyleURL)];
}
-- (void)testLoadingSatelliteStreetsStyleURL {
+- (void)testLoadingSatelliteStreetsStyleURL🔒 {
[self internalTestWithStyleSelector:@selector(satelliteStreetsStyleURL)];
}
diff --git a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift
index 0f2034b6b8..d33a986beb 100644
--- a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift
+++ b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterSwiftTests.swift
@@ -15,12 +15,9 @@ class MGLMapSnapshotterSwiftTests: MGLMapViewIntegrationTest {
return options
}
- func testCapturingSnapshotterInSnapshotCompletion() {
+ func testCapturingSnapshotterInSnapshotCompletion🔒() {
// See the Obj-C testDeallocatingSnapshotterDuringSnapshot
// This Swift test, is essentially the same except for capturing the snapshotter
- guard validAccessToken() != nil else {
- return
- }
let timeout: TimeInterval = 10.0
let expectation = self.expectation(description: "snapshot")
@@ -59,4 +56,22 @@ class MGLMapSnapshotterSwiftTests: MGLMapViewIntegrationTest {
wait(for: [expectation], timeout: timeout)
}
+
+ func testSnapshotOverlaySwiftErgonomics🔒() {
+ let options = MGLMapSnapshotterSwiftTests.snapshotterOptions(size: mapView.bounds.size)
+ let snapshotter = MGLMapSnapshotter(options: options)
+ let expectation = self.expectation(description: "snapshot")
+ expectation.expectedFulfillmentCount = 2
+
+ snapshotter.start(overlayHandler: { (overlay) in
+ guard let _ = overlay.context.makeImage() else {
+ XCTFail()
+ return
+ }
+ expectation.fulfill()
+ }) { (_, _) in
+ expectation.fulfill()
+ }
+ wait(for: [expectation], timeout: 10)
+ }
}
diff --git a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m
index 32e5fc782d..19718165b3 100644
--- a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m
+++ b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m
@@ -26,11 +26,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
@implementation MGLMapSnapshotterTest
-- (void)testMultipleSnapshotsWithASingleSnapshotter {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testMultipleSnapshotsWithASingleSnapshotter🔒 {
CGSize size = self.mapView.bounds.size;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshots"];
@@ -60,11 +56,8 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:10.0];
}
-- (void)testDeallocatingSnapshotterDuringSnapshot {
+- (void)testDeallocatingSnapshotterDuringSnapshot🔒 {
// See also https://github.com/mapbox/mapbox-gl-native/issues/12336
- if (![self validAccessToken]) {
- return;
- }
NSTimeInterval timeout = 10.0;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
@@ -108,16 +101,12 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:timeout];
}
-- (void)testSnapshotterUsingNestedDispatchQueues {
+- (void)testSnapshotterUsingNestedDispatchQueues🔒 {
// This is the opposite pair to the above test `testDeallocatingSnapshotterDuringSnapshot`
// The only significant difference is that the snapshotter is a `__block` variable, so
// its lifetime should continue until it's set to nil in the completion block.
// See also https://github.com/mapbox/mapbox-gl-native/issues/12336
- if (![self validAccessToken]) {
- return;
- }
-
NSTimeInterval timeout = 10.0;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
CGSize size = self.mapView.bounds.size;
@@ -156,11 +145,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:timeout];
}
-- (void)testCancellingSnapshot {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testCancellingSnapshot🔒 {
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshots"];
expectation.assertForOverFulfill = YES;
expectation.expectedFulfillmentCount = 1;
@@ -189,11 +174,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:5.0];
}
-- (void)testAllocatingSnapshotOnBackgroundQueue {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testAllocatingSnapshotOnBackgroundQueue🔒 {
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshots"];
CGSize size = self.mapView.bounds.size;
@@ -226,11 +207,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:2.0];
}
-- (void)testSnapshotterFromBackgroundQueueShouldFail {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testSnapshotterFromBackgroundQueueShouldFail🔒 {
CGSize size = self.mapView.bounds.size;
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0);
@@ -281,12 +258,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:60.0];
}
-- (void)testMultipleSnapshottersPENDING {
-
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testMultipleSnapshotters🔒🙁 {
NSUInteger numSnapshots = 8;
CGSize size = self.mapView.bounds.size;
@@ -340,11 +312,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:60.0];
}
-- (void)testSnapshotPointConversion {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testSnapshotPointConversion🔒 {
CGSize size = self.mapView.bounds.size;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
@@ -382,11 +350,7 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:10.0];
}
-- (void)testSnapshotPointConversionCoordinateOrdering {
- if (![self validAccessToken]) {
- return;
- }
-
+- (void)testSnapshotPointConversionCoordinateOrdering🔒 {
CGSize size = self.mapView.bounds.size;
XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot"];
@@ -429,5 +393,73 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:10.0];
}
+- (void)testSnapshotWithOverlayHandlerFailure🔒 {
+ CGSize size = self.mapView.bounds.size;
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot with overlay fails"];
+ expectation.expectedFulfillmentCount = 2;
+
+ CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0);
+
+ MGLMapSnapshotter *snapshotter = snapshotterWithCoordinates(coord, size);
+ XCTAssertNotNil(snapshotter);
+
+ [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay *snapshotOverlay) {
+ XCTAssertNotNil(snapshotOverlay);
+
+ UIGraphicsEndImageContext();
+ [expectation fulfill];
+ } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ XCTAssertNil(snapshot);
+ XCTAssertNotNil(error);
+ XCTAssertEqualObjects(error.domain, MGLErrorDomain);
+ XCTAssertEqual(error.code, MGLErrorCodeSnapshotFailed);
+ XCTAssertEqualObjects(error.localizedDescription, @"Failed to generate composited snapshot.");
+
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:10.0];
+}
+
+- (void)testSnapshotWithOverlayHandlerSuccess🔒 {
+ CGSize size = self.mapView.bounds.size;
+ CGRect snapshotRect = CGRectMake(0, 0, size.width, size.height);
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot with overlay succeeds"];
+ expectation.expectedFulfillmentCount = 2;
+
+ CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(30.0, 30.0);
+
+ MGLMapSnapshotter *snapshotter = snapshotterWithCoordinates(coord, size);
+ XCTAssertNotNil(snapshotter);
+
+ CGFloat scale = snapshotter.options.scale;
+
+ [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay *snapshotOverlay) {
+ XCTAssertNotNil(snapshotOverlay);
+
+ CGFloat width = CGBitmapContextGetWidth(snapshotOverlay.context);
+ CGFloat height = CGBitmapContextGetHeight(snapshotOverlay.context);
+
+ CGRect contextRect = CGContextConvertRectToDeviceSpace(snapshotOverlay.context, CGRectMake(0, 0, 1, 0));
+ CGFloat scaleFromContext = contextRect.size.width;
+ XCTAssertEqual(scale, scaleFromContext);
+ XCTAssertEqual(width, size.width*scale);
+ XCTAssertEqual(height, size.height*scale);
+
+ CGContextSetFillColorWithColor(snapshotOverlay.context, [UIColor.greenColor CGColor]);
+ CGContextSetAlpha(snapshotOverlay.context, 0.2);
+ CGContextAddRect(snapshotOverlay.context, snapshotRect);
+ CGContextFillRect(snapshotOverlay.context, snapshotRect);
+ [expectation fulfill];
+ } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ XCTAssertNil(error);
+ XCTAssertNotNil(snapshot);
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:10.0];
+}
@end
diff --git a/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec
index 6ce78ed67e..52dbe44428 100644
--- a/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-snapshot-dynamic.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '5.3.0-alpha.1'
+ version = '5.4.0-alpha.2'
m.name = 'Mapbox-iOS-SDK-snapshot-dynamic'
m.version = "#{version}-snapshot"
diff --git a/platform/ios/Mapbox-iOS-SDK-stripped.podspec b/platform/ios/Mapbox-iOS-SDK-stripped.podspec
index 548a1c458e..a86ef2aef3 100644
--- a/platform/ios/Mapbox-iOS-SDK-stripped.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-stripped.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '5.3.0-alpha.1'
+ version = '5.4.0-alpha.2'
m.name = 'Mapbox-iOS-SDK-stripped'
m.version = "#{version}-stripped"
diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec
index 9378eed999..b0e71ef5e6 100644
--- a/platform/ios/Mapbox-iOS-SDK.podspec
+++ b/platform/ios/Mapbox-iOS-SDK.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '5.3.0-alpha.1'
+ version = '5.4.0-alpha.2'
m.name = 'Mapbox-iOS-SDK'
m.version = version
diff --git a/platform/ios/Mapbox.playground/Contents.swift b/platform/ios/Mapbox.playground/Contents.swift
index f31c9b6171..3bdb870fb5 100644
--- a/platform/ios/Mapbox.playground/Contents.swift
+++ b/platform/ios/Mapbox.playground/Contents.swift
@@ -121,10 +121,9 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
let isRecognized = press.state == .recognized
if (isRecognized) {
- let coordinate: CLLocationCoordinate2D = mapView.convert(press.location(in: mapView), toCoordinateFrom: mapView)
let annotation = MGLPointAnnotation()
annotation.title = "Dropped Marker"
- annotation.coordinate = coordinate
+ annotation.coordinate = mapView.convert(press.location(in: mapView), toCoordinateFrom: mapView)
mapView.addAnnotation(annotation)
mapView.showAnnotations([annotation], animated: true)
}
@@ -134,8 +133,6 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
//: Create a map and its delegate
-let centerCoordinate = CLLocationCoordinate2D(latitude: 37.174057, longitude: -104.490984)
-
let mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: width, height: height))
mapView.frame = CGRect(x: 0, y: 0, width: width, height: height)
@@ -150,7 +147,7 @@ mapView.addGestureRecognizer(tapGesture)
//: Zoom in to a location
-mapView.setCenter(centerCoordinate, zoomLevel: 12, animated: false)
+mapView.setCenter(CLLocationCoordinate2D(latitude: 37.174057, longitude: -104.490984), zoomLevel: 12, animated: false)
//: Add control panel
diff --git a/platform/ios/Mapbox.playground/contents.xcplayground b/platform/ios/Mapbox.playground/contents.xcplayground
index 35968656f5..c8659f26e4 100644
--- a/platform/ios/Mapbox.playground/contents.xcplayground
+++ b/platform/ios/Mapbox.playground/contents.xcplayground
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<playground version='5.0' target-platform='ios' display-mode='raw'>
+<playground version='5.0' target-platform='ios' display-mode='raw' last-migration='1100'>
<timeline fileName='timeline.xctimeline'/>
</playground> \ No newline at end of file
diff --git a/platform/ios/app/MBXAnnotationView.h b/platform/ios/app/MBXAnnotationView.h
index 5337ffae57..6bd799673d 100644
--- a/platform/ios/app/MBXAnnotationView.h
+++ b/platform/ios/app/MBXAnnotationView.h
@@ -1,4 +1,4 @@
-#import <Mapbox/Mapbox.h>
+@import Mapbox;
@interface MBXAnnotationView : MGLAnnotationView
@end
diff --git a/platform/ios/app/MBXAppDelegate.m b/platform/ios/app/MBXAppDelegate.m
index 519c70766a..bf62866b8e 100644
--- a/platform/ios/app/MBXAppDelegate.m
+++ b/platform/ios/app/MBXAppDelegate.m
@@ -1,6 +1,7 @@
+@import Mapbox;
+
#import "MBXAppDelegate.h"
#import "MBXViewController.h"
-#import <Mapbox/Mapbox.h>
@interface MBXAppDelegate() <MGLMetricsManagerDelegate>
diff --git a/platform/ios/app/MBXCustomCalloutView.h b/platform/ios/app/MBXCustomCalloutView.h
index 961f2d7f6a..5a7c9c926f 100644
--- a/platform/ios/app/MBXCustomCalloutView.h
+++ b/platform/ios/app/MBXCustomCalloutView.h
@@ -1,5 +1,4 @@
-#import <UIKit/UIKit.h>
-#import <Mapbox/Mapbox.h>
+@import Mapbox;
/**
* Basic custom callout view to demonstrate how to
diff --git a/platform/ios/app/MBXCustomLocationViewController.m b/platform/ios/app/MBXCustomLocationViewController.m
index 0473f8c2ef..c77d990bac 100644
--- a/platform/ios/app/MBXCustomLocationViewController.m
+++ b/platform/ios/app/MBXCustomLocationViewController.m
@@ -1,6 +1,6 @@
-#import "MBXCustomLocationViewController.h"
+@import Mapbox;
-#import <Mapbox/Mapbox.h>
+#import "MBXCustomLocationViewController.h"
@interface MBXCustomLocationManager : NSObject<MGLLocationManager>
@end
diff --git a/platform/ios/app/MBXEmbeddedMapViewController.m b/platform/ios/app/MBXEmbeddedMapViewController.m
index f26a007c15..dacbdb5124 100644
--- a/platform/ios/app/MBXEmbeddedMapViewController.m
+++ b/platform/ios/app/MBXEmbeddedMapViewController.m
@@ -1,5 +1,6 @@
+@import Mapbox;
+
#import "MBXEmbeddedMapViewController.h"
-#import <Mapbox/Mapbox.h>
typedef NS_ENUM(NSInteger, MBXEmbeddedControl) {
MBXEmbeddedControlZoom = 0,
diff --git a/platform/ios/app/MBXOfflinePacksTableViewController.m b/platform/ios/app/MBXOfflinePacksTableViewController.m
index 90497ca939..bc8407a46d 100644
--- a/platform/ios/app/MBXOfflinePacksTableViewController.m
+++ b/platform/ios/app/MBXOfflinePacksTableViewController.m
@@ -1,6 +1,7 @@
+@import Mapbox;
+
#import "MBXOfflinePacksTableViewController.h"
-#import <Mapbox/Mapbox.h>
static NSString * const MBXOfflinePackContextNameKey = @"Name";
diff --git a/platform/ios/app/MBXOrnamentsViewController.m b/platform/ios/app/MBXOrnamentsViewController.m
index 861d5d7678..f451f647e5 100644
--- a/platform/ios/app/MBXOrnamentsViewController.m
+++ b/platform/ios/app/MBXOrnamentsViewController.m
@@ -1,7 +1,7 @@
-#import "MBXOrnamentsViewController.h"
-
@import Mapbox;
+#import "MBXOrnamentsViewController.h"
+
@interface MBXOrnamentsViewController ()<MGLMapViewDelegate>
@property (nonatomic) MGLMapView *mapView;
diff --git a/platform/ios/app/MBXSnapshotsViewController.m b/platform/ios/app/MBXSnapshotsViewController.m
index 95d3251e2e..747ff39248 100644
--- a/platform/ios/app/MBXSnapshotsViewController.m
+++ b/platform/ios/app/MBXSnapshotsViewController.m
@@ -1,6 +1,6 @@
-#import "MBXSnapshotsViewController.h"
+@import Mapbox;
-#import <Mapbox/Mapbox.h>
+#import "MBXSnapshotsViewController.h"
@interface MBXSnapshotsViewController ()
diff --git a/platform/ios/app/MBXState.h b/platform/ios/app/MBXState.h
index 7cf064acd8..cf68016e98 100644
--- a/platform/ios/app/MBXState.h
+++ b/platform/ios/app/MBXState.h
@@ -1,5 +1,4 @@
-#import <Foundation/Foundation.h>
-#import <Mapbox/Mapbox.h>
+@import Mapbox;
NS_ASSUME_NONNULL_BEGIN
@@ -12,7 +11,6 @@ FOUNDATION_EXTERN NSString *const MBXShowsZoomLevelOrnament;
FOUNDATION_EXTERN NSString *const MBXShowsTimeFrameGraph;
FOUNDATION_EXTERN NSString *const MBXMapFramerateMeasurementEnabled;
FOUNDATION_EXTERN NSString *const MBXDebugMaskValue;
-FOUNDATION_EXTERN NSString *const MBXDebugLoggingEnabled;
FOUNDATION_EXTERN NSString *const MBXReuseQueueStatsEnabled;
@interface MBXState : NSObject <NSSecureCoding>
@@ -26,7 +24,6 @@ FOUNDATION_EXTERN NSString *const MBXReuseQueueStatsEnabled;
@property (nonatomic) BOOL showsTimeFrameGraph;
@property (nonatomic) BOOL framerateMeasurementEnabled;
@property (nonatomic) MGLMapDebugMaskOptions debugMask;
-@property (nonatomic) BOOL debugLoggingEnabled;
@property (nonatomic) BOOL reuseQueueStatsEnabled;
@property (nonatomic, readonly) NSString *debugDescription;
diff --git a/platform/ios/app/MBXState.m b/platform/ios/app/MBXState.m
index 0365306637..455961e88a 100644
--- a/platform/ios/app/MBXState.m
+++ b/platform/ios/app/MBXState.m
@@ -6,7 +6,6 @@ NSString *const MBXShowsUserLocation = @"MBXShowsUserLocation";
NSString *const MBXDebugMaskValue = @"MBXDebugMaskValue";
NSString *const MBXShowsZoomLevelOrnament = @"MBXShowsZoomLevelOrnament";
NSString *const MBXShowsTimeFrameGraph = @"MBXShowsFrameTimeGraph";
-NSString *const MBXDebugLoggingEnabled = @"MGLMapboxMetricsDebugLoggingEnabled";
NSString *const MBXShowsMapScale = @"MBXMapShowsScale";
NSString *const MBXMapShowsHeadingIndicator = @"MBXMapShowsHeadingIndicator";
NSString *const MBXMapFramerateMeasurementEnabled = @"MBXMapFramerateMeasurementEnabled";
@@ -26,7 +25,6 @@ NSString *const MBXReuseQueueStatsEnabled = @"MBXReuseQueueStatsEnabled";
[coder encodeObject:[NSNumber numberWithUnsignedInteger:_debugMask] forKey:MBXDebugMaskValue];
[coder encodeBool:_showsZoomLevelOrnament forKey:MBXShowsZoomLevelOrnament];
[coder encodeBool:_showsTimeFrameGraph forKey:MBXShowsTimeFrameGraph];
- [coder encodeBool:_debugLoggingEnabled forKey:MBXDebugLoggingEnabled];
[coder encodeBool:_showsMapScale forKey:MBXShowsMapScale];
[coder encodeBool:_showsUserHeadingIndicator forKey:MBXMapShowsHeadingIndicator];
[coder encodeBool:_framerateMeasurementEnabled forKey:MBXMapFramerateMeasurementEnabled];
@@ -41,7 +39,6 @@ NSString *const MBXReuseQueueStatsEnabled = @"MBXReuseQueueStatsEnabled";
NSNumber *decodedDebugMaskOptions = [decoder decodeObjectForKey:MBXDebugMaskValue];
BOOL decodedZoomLevelOrnament = [decoder decodeBoolForKey:MBXShowsZoomLevelOrnament];
BOOL decodedShowsTimeFrameGraph = [decoder decodeBoolForKey:MBXShowsTimeFrameGraph];
- BOOL decodedDebugLoggingEnabled = [decoder decodeBoolForKey:MBXDebugLoggingEnabled];
BOOL decodedShowsMapScale = [decoder decodeBoolForKey:MBXShowsMapScale];
BOOL decodedShowsUserHeadingIndicator = [decoder decodeBoolForKey:MBXMapShowsHeadingIndicator];
BOOL decodedFramerateMeasurementEnabled = [decoder decodeBoolForKey:MBXMapFramerateMeasurementEnabled];
@@ -53,7 +50,6 @@ NSString *const MBXReuseQueueStatsEnabled = @"MBXReuseQueueStatsEnabled";
_debugMask = decodedDebugMaskOptions.intValue;
_showsZoomLevelOrnament = decodedZoomLevelOrnament;
_showsTimeFrameGraph = decodedShowsTimeFrameGraph;
- _debugLoggingEnabled = decodedDebugLoggingEnabled;
_showsMapScale = decodedShowsMapScale;
_showsUserHeadingIndicator = decodedShowsUserHeadingIndicator;
_framerateMeasurementEnabled = decodedFramerateMeasurementEnabled;
@@ -67,15 +63,14 @@ NSString *const MBXReuseQueueStatsEnabled = @"MBXReuseQueueStatsEnabled";
return YES;
}
-- (NSString*) debugDescription {
- return [NSString stringWithFormat:@"Camera: %@\nTracking mode: %lu\nShows user location: %@\nDebug mask value: %lu\nShows zoom level ornament: %@\nShows time frame graph: %@\nDebug logging enabled: %@\nShows map scale: %@\nShows user heading indicator: %@\nFramerate measurement enabled: %@\nReuse queue stats enabled: %@",
+- (NSString *)debugDescription {
+ return [NSString stringWithFormat:@"Camera: %@\nTracking mode: %lu\nShows user location: %@\nDebug mask value: %lu\nShows zoom level ornament: %@\nShows time frame graph: %@\nShows map scale: %@\nShows user heading indicator: %@\nFramerate measurement enabled: %@\nReuse queue stats enabled: %@",
self.camera,
(unsigned long)self.userTrackingMode,
self.showsUserLocation ? @"YES" : @"NO",
(unsigned long)self.debugMask,
self.showsZoomLevelOrnament ? @"YES" : @"NO",
self.showsTimeFrameGraph ? @"YES" : @"NO",
- self.debugLoggingEnabled ? @"YES" : @"NO",
self.showsMapScale ? @"YES" : @"NO",
self.showsUserHeadingIndicator ? @"YES" : @"NO",
self.framerateMeasurementEnabled ? @"YES" : @"NO",
diff --git a/platform/ios/app/MBXStateManager.m b/platform/ios/app/MBXStateManager.m
index 7203ae462d..f9dc771e26 100644
--- a/platform/ios/app/MBXStateManager.m
+++ b/platform/ios/app/MBXStateManager.m
@@ -1,5 +1,4 @@
#import "MBXStateManager.h"
-#import <Mapbox/Mapbox.h>
#import "MBXState.h"
#import "MBXViewController.h"
diff --git a/platform/ios/app/MBXUserLocationAnnotationView.h b/platform/ios/app/MBXUserLocationAnnotationView.h
index 39ed729d2b..a60658dd18 100644
--- a/platform/ios/app/MBXUserLocationAnnotationView.h
+++ b/platform/ios/app/MBXUserLocationAnnotationView.h
@@ -1,4 +1,4 @@
-#import <Mapbox/Mapbox.h>
+@import Mapbox;
@interface MBXUserLocationAnnotationView : MGLUserLocationAnnotationView
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 2fb95e1b17..82a68e074a 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -1,3 +1,5 @@
+@import Mapbox;
+
#import "MBXViewController.h"
#import "MBXAppDelegate.h"
@@ -12,8 +14,6 @@
#import "MBXState.h"
#import "MBXFrameTimeGraphView.h"
-
-#import <Mapbox/Mapbox.h>
#import "../src/MGLMapView_Experimental.h"
#import <objc/runtime.h>
@@ -211,7 +211,6 @@ CLLocationCoordinate2D randomWorldCoordinate() {
@property (nonatomic) BOOL shouldLimitCameraChanges;
@property (nonatomic) BOOL randomWalk;
@property (nonatomic) BOOL zoomLevelOrnamentEnabled;
-@property (nonatomic) BOOL debugLoggingEnabled;
@property (nonatomic) NSMutableArray<UIWindow *> *helperWindows;
@property (nonatomic) NSMutableArray<UIView *> *contentInsetsOverlays;
@@ -246,7 +245,6 @@ CLLocationCoordinate2D randomWorldCoordinate() {
self.mapView.showsScale = YES;
self.zoomLevelOrnamentEnabled = NO;
self.frameTimeGraphEnabled = NO;
- self.debugLoggingEnabled = YES;
} else {
// Revert to the previously saved state
[self restoreMapState:nil];
@@ -441,14 +439,6 @@ CLLocationCoordinate2D randomWorldCoordinate() {
@"Ornaments Placement",
]];
- if (self.currentState.debugLoggingEnabled)
- {
- [settingsTitles addObjectsFromArray:@[
- @"Print Telemetry Logfile",
- @"Delete Telemetry Logfile",
- ]];
- };
-
break;
default:
NSAssert(NO, @"All settings sections should be implemented");
@@ -558,15 +548,12 @@ CLLocationCoordinate2D randomWorldCoordinate() {
case MBXSettingsAnnotationSelectRandomOffscreenPointAnnotation:
[self selectAnOffscreenPointAnnotation];
break;
-
case MBXSettingsAnnotationCenterSelectedAnnotation:
[self centerSelectedAnnotation];
break;
-
case MBXSettingsAnnotationAddVisibleAreaPolyline:
[self addVisibleAreaPolyline];
break;
-
default:
NSAssert(NO, @"All annotations setting rows should be implemented");
break;
@@ -667,13 +654,6 @@ CLLocationCoordinate2D randomWorldCoordinate() {
case MBXSettingsMiscellaneousRandomTour:
[self randomWorldTour];
break;
-
- case MBXSettingsMiscellaneousPrintLogFile:
- [self printTelemetryLogFile];
- break;
- case MBXSettingsMiscellaneousDeleteLogFile:
- [self deleteTelemetryLogFile];
- break;
case MBXSettingsMiscellaneousScrollView:
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
@@ -749,7 +729,7 @@ CLLocationCoordinate2D randomWorldCoordinate() {
acrossDistance:100
pitch:60
heading:0];
- __weak typeof(self) weakSelf = self;
+ __weak MBXViewController *weakSelf = self;
[self.mapView setCamera:camera withDuration:0.3 animationTimingFunction:nil completionHandler:^{
[weakSelf.mapView setContentInset:contentInsets animated:YES completionHandler:nil];
}];
@@ -1734,37 +1714,6 @@ CLLocationCoordinate2D randomWorldCoordinate() {
return backupImage;
}
-- (void)printTelemetryLogFile
-{
- NSString *fileContents = [NSString stringWithContentsOfFile:[self telemetryDebugLogFilePath] encoding:NSUTF8StringEncoding error:nil];
- NSLog(@"%@", fileContents);
-}
-
-- (void)deleteTelemetryLogFile
-{
- NSString *filePath = [self telemetryDebugLogFilePath];
- if ([[NSFileManager defaultManager] isDeletableFileAtPath:filePath])
- {
- NSError *error;
- BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
- if (success) {
- NSLog(@"Deleted telemetry log.");
- } else {
- NSLog(@"Error deleting telemetry log: %@", error.localizedDescription);
- }
- }
-}
-
-- (NSString *)telemetryDebugLogFilePath
-{
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
- [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
- NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"telemetry_log-%@.json", [dateFormatter stringFromDate:[NSDate date]]]];
-
- return filePath;
-}
-
#pragma mark - Random World Tour
- (void)addAnnotations:(NSInteger)numAnnotations aroundCoordinate:(CLLocationCoordinate2D)coordinate radius:(CLLocationDistance)radius {
@@ -2396,7 +2345,6 @@ CLLocationCoordinate2D randomWorldCoordinate() {
self.currentState.showsZoomLevelOrnament = self.zoomLevelOrnamentEnabled;
self.currentState.showsTimeFrameGraph = self.frameTimeGraphEnabled;
self.currentState.debugMask = self.mapView.debugMask;
- self.currentState.debugLoggingEnabled = self.debugLoggingEnabled;
self.currentState.reuseQueueStatsEnabled = self.reuseQueueStatsEnabled;
[[MBXStateManager sharedManager] saveState:self.currentState];
@@ -2413,7 +2361,6 @@ CLLocationCoordinate2D randomWorldCoordinate() {
self.zoomLevelOrnamentEnabled = currentState.showsZoomLevelOrnament;
self.frameTimeGraphEnabled = currentState.showsTimeFrameGraph;
self.mapView.debugMask = currentState.debugMask;
- self.debugLoggingEnabled = currentState.debugLoggingEnabled;
self.reuseQueueStatsEnabled = currentState.reuseQueueStatsEnabled;
self.currentState = currentState;
diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard
index 28316745a1..ac83bd968f 100644
--- a/platform/ios/app/Main.storyboard
+++ b/platform/ios/app/Main.storyboard
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14810.11" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="PSe-Ot-7Ff">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="PSe-Ot-7Ff">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14766.13"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -13,11 +13,11 @@
<objects>
<viewController id="WaX-pd-UZQ" userLabel="Map View Controller" customClass="MBXViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Z9X-fc-PUC">
- <rect key="frame" x="0.0" y="0.0" width="203" height="33"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kNe-zV-9ha" customClass="MGLMapView">
- <rect key="frame" x="0.0" y="0.0" width="203" height="33"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<button hidden="YES" opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="58y-pX-YyB">
<rect key="frame" x="8" y="82" width="40" height="20"/>
@@ -38,7 +38,7 @@
</userDefinedRuntimeAttributes>
</button>
<view hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BHE-Wn-x69" customClass="MBXFrameTimeGraphView">
- <rect key="frame" x="0.0" y="-167" width="203" height="200"/>
+ <rect key="frame" x="0.0" y="467" width="375" height="200"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" notEnabled="YES"/>
@@ -84,7 +84,7 @@
</connections>
</barButtonItem>
<button key="titleView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="KsN-ny-Hou">
- <rect key="frame" x="65" y="5.5" width="203" height="33"/>
+ <rect key="frame" x="89" y="5.5" width="148" height="33"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<state key="normal" title="Streets"/>
@@ -198,7 +198,7 @@
<action selector="addCurrentRegion:" destination="7q0-lI-zqb" id="G2O-3V-aEA"/>
</connections>
</barButtonItem>
- <barButtonItem style="plain" systemItem="refresh" id="2fx-iS-Veb">
+ <barButtonItem systemItem="refresh" id="2fx-iS-Veb">
<connections>
<action selector="invalidatePacks:" destination="7q0-lI-zqb" id="5lx-FY-aTt"/>
</connections>
diff --git a/platform/ios/benchmark/MBXBenchAppDelegate.m b/platform/ios/benchmark/MBXBenchAppDelegate.m
index 522ebb2dbc..d1a0ddf943 100644
--- a/platform/ios/benchmark/MBXBenchAppDelegate.m
+++ b/platform/ios/benchmark/MBXBenchAppDelegate.m
@@ -1,6 +1,5 @@
#import "MBXBenchAppDelegate.h"
#import "MBXBenchViewController.h"
-#import <Mapbox/Mapbox.h>
@implementation MBXBenchAppDelegate
diff --git a/platform/ios/benchmark/MBXBenchViewController.mm b/platform/ios/benchmark/MBXBenchViewController.mm
index 67d9b5cb6e..901eb07cd1 100644
--- a/platform/ios/benchmark/MBXBenchViewController.mm
+++ b/platform/ios/benchmark/MBXBenchViewController.mm
@@ -1,8 +1,5 @@
#import "MBXBenchViewController.h"
-
#import "MBXBenchAppDelegate.h"
-
-#import <Mapbox/Mapbox.h>
#import "MGLMapView_Private.h"
#include "locations.hpp"
diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake
index e5386e4ae0..7f15355876 100644
--- a/platform/ios/config.cmake
+++ b/platform/ios/config.cmake
@@ -13,7 +13,7 @@ endmacro()
include(cmake/loop-darwin.cmake)
-initialize_ios_target(icu)
+initialize_ios_target(mbgl-vendor-icu)
initialize_ios_target(mbgl-loop-darwin)
diff --git a/platform/ios/core-files.json b/platform/ios/core-files.json
index 93fdfc80b9..67edda9733 100644
--- a/platform/ios/core-files.json
+++ b/platform/ios/core-files.json
@@ -25,8 +25,7 @@
"mbgl/gfx/headless_backend.hpp": "platform/default/include/mbgl/gfx/headless_backend.hpp",
"mbgl/gfx/headless_frontend.hpp": "platform/default/include/mbgl/gfx/headless_frontend.hpp",
"mbgl/gl/headless_backend.hpp": "platform/default/include/mbgl/gl/headless_backend.hpp",
- "mbgl/map/map_snapshotter.hpp": "platform/default/include/mbgl/map/map_snapshotter.hpp",
- "mbgl/util/default_styles.hpp": "platform/default/include/mbgl/util/default_styles.hpp"
+ "mbgl/map/map_snapshotter.hpp": "platform/default/include/mbgl/map/map_snapshotter.hpp"
},
"private_headers": {
"CFHandle.hpp": "platform/darwin/src/CFHandle.hpp"
diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md
index 1acf587cda..d6259238fe 100644
--- a/platform/ios/docs/guides/For Style Authors.md
+++ b/platform/ios/docs/guides/For Style Authors.md
@@ -270,6 +270,7 @@ In style JSON | In Objective-C | In Swift
`text-justify` | `MGLSymbolStyleLayer.textJustification` | `MGLSymbolStyleLayer.textJustification`
`text-optional` | `MGLSymbolStyleLayer.textOptional` | `MGLSymbolStyleLayer.isTextOptional`
`text-rotate` | `MGLSymbolStyleLayer.textRotation` | `MGLSymbolStyleLayer.textRotation`
+`text-writing-mode` | `MGLSymbolStyleLayer.textWritingModes` | `MGLSymbolStyleLayer.textWritingModes`
`icon-translate` | `MGLSymbolStyleLayer.iconTranslation` | `MGLSymbolStyleLayer.iconTranslation`
`icon-translate-anchor` | `MGLSymbolStyleLayer.iconTranslationAnchor` | `MGLSymbolStyleLayer.iconTranslationAnchor`
`text-translate` | `MGLSymbolStyleLayer.textTranslation` | `MGLSymbolStyleLayer.textTranslation`
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index de2bec5d99..9ac5d564fe 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -276,8 +276,8 @@
558DE7A11E5615E400C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */; };
558DE7A21E5615E400C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE79F1E5615E400C7916D /* MGLFoundation.mm */; };
558DE7A31E5615E400C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE79F1E5615E400C7916D /* MGLFoundation.mm */; };
- 55CF752F213ED92000ED86C4 /* libicu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CF752E213ED92000ED86C4 /* libicu.a */; };
- 55CF7531213ED92A00ED86C4 /* libicu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CF7530213ED92A00ED86C4 /* libicu.a */; };
+ 55CF752F213ED92000ED86C4 /* libmbgl-vendor-icu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CF752E213ED92000ED86C4 /* libmbgl-vendor-icu.a */; };
+ 55CF7531213ED92A00ED86C4 /* libmbgl-vendor-icu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CF7530213ED92A00ED86C4 /* libmbgl-vendor-icu.a */; };
55D120A61F791007004B6D81 /* libmbgl-loop-darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */; };
55D120A81F79100C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */; };
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */; };
@@ -388,7 +388,7 @@
9680274022653B84006BA4A1 /* MBXSKUToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 9680273E22653B84006BA4A1 /* MBXSKUToken.h */; };
9680276422655696006BA4A1 /* libmbxaccounts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9680274122653C3E006BA4A1 /* libmbxaccounts.a */; };
96802766226556C5006BA4A1 /* libmbxaccounts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9680274122653C3E006BA4A1 /* libmbxaccounts.a */; };
- 9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.m */; };
+ 9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.mm */; };
968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3557F7AE1E1D27D300CCA5E6 /* MGLDistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
96E027231E57C76E004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027251E57C76E004B8E66 /* Localizable.strings */; };
96E516DC2000547000A02306 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; };
@@ -481,6 +481,8 @@
9C6E284522A982670056B7BE /* MMEUINavigation.h in Headers */ = {isa = PBXBuildFile; fileRef = 406E99B31FFEFED600D9FFCC /* MMEUINavigation.h */; };
9C6E284622A982670056B7BE /* MMEUniqueIdentifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 40834BBF1FE05D6E00C1BD0D /* MMEUniqueIdentifier.h */; };
9C6E284722A982670056B7BE /* MMEDispatchManager.h in Headers */ = {isa = PBXBuildFile; fileRef = ACA65F552140696B00537748 /* MMEDispatchManager.h */; };
+ A4DE3DCB23038C98005B3473 /* MGLMockGestureRecognizers.h in Sources */ = {isa = PBXBuildFile; fileRef = A4DE3DCA23038A7F005B3473 /* MGLMockGestureRecognizers.h */; };
+ A4DE3DCC23038CCA005B3473 /* MGLMockGestureRecognizers.m in Sources */ = {isa = PBXBuildFile; fileRef = A4DE3DC823038A07005B3473 /* MGLMockGestureRecognizers.m */; };
A4F3FB1D2254865900A30170 /* missing_icon.json in Resources */ = {isa = PBXBuildFile; fileRef = A4F3FB1C2254865900A30170 /* missing_icon.json */; };
AC46EB59225E600A0039C013 /* MMECertPin.h in Headers */ = {isa = PBXBuildFile; fileRef = AC46EB57225E60090039C013 /* MMECertPin.h */; };
AC46EB5A225E600A0039C013 /* MMECertPin.h in Headers */ = {isa = PBXBuildFile; fileRef = AC46EB57225E60090039C013 /* MMECertPin.h */; };
@@ -504,13 +506,16 @@
CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */; };
CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */; };
CA1B4A512099FB2200EDD491 /* MGLMapSnapshotterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA1B4A502099FB2200EDD491 /* MGLMapSnapshotterTest.m */; };
- CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */; };
+ CA4C54FE2324948100A81659 /* MGLSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4C54FD2324948100A81659 /* MGLSourceTests.swift */; };
CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */; };
+ CA4F3BDE230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */; };
+ CA4F3BE223107793008BAFEA /* MGLCameraTransitionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA4F3BE123107793008BAFEA /* MGLCameraTransitionTests.mm */; };
+ CA4F3BE4231077B9008BAFEA /* MGLCameraTransitionFinishTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA4F3BE3231077B9008BAFEA /* MGLCameraTransitionFinishTests.mm */; };
CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA65C4F821E9BB080068B0D4 /* MGLCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = CA65C4F721E9BB080068B0D4 /* MGLCluster.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA65C4F921E9BB080068B0D4 /* MGLCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = CA65C4F721E9BB080068B0D4 /* MGLCluster.h */; settings = {ATTRIBUTES = (Public, ); }; };
- CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m */; };
+ CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */; };
CA7766832229C10E0008DE9E /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; };
CA7766842229C11A0008DE9E /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */; };
CA86FF0E22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */; };
@@ -572,8 +577,8 @@
DA5DB12A1FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */; };
DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA6408DC1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; };
- DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; };
+ DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.mm */; };
+ DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.mm */; };
DA695426215B1E76002041A4 /* MGLMapCameraTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA695425215B1E75002041A4 /* MGLMapCameraTests.m */; };
DA704CC21F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */; };
DA704CC31F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */; };
@@ -1065,8 +1070,8 @@
5580B45A229570A10091291B /* MGLMapView+OpenGL.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+OpenGL.mm"; sourceTree = "<group>"; };
558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = "<group>"; };
558DE79F1E5615E400C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = "<group>"; };
- 55CF752E213ED92000ED86C4 /* libicu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libicu.a; sourceTree = BUILT_PRODUCTS_DIR; };
- 55CF7530213ED92A00ED86C4 /* libicu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libicu.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55CF752E213ED92000ED86C4 /* libmbgl-vendor-icu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libmbgl-vendor-icu.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55CF7530213ED92A00ED86C4 /* libmbgl-vendor-icu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libmbgl-vendor-icu.a; sourceTree = BUILT_PRODUCTS_DIR; };
55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
55D120A91F79100C004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
55D8C9941D0F133500F42F10 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/ios/config.xcconfig; sourceTree = "<group>"; };
@@ -1129,7 +1134,7 @@
967C864A210A9D3C004DF794 /* UIDevice+MGLAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+MGLAdditions.m"; sourceTree = "<group>"; };
9680273E22653B84006BA4A1 /* MBXSKUToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MBXSKUToken.h; path = "../vendor/mapbox-accounts-ios/MBXSKUToken.h"; sourceTree = "<group>"; };
9680274122653C3E006BA4A1 /* libmbxaccounts.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbxaccounts.a; path = "vendor/mapbox-accounts-ios/libmbxaccounts.a"; sourceTree = SOURCE_ROOT; };
- 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewZoomTests.m; sourceTree = "<group>"; };
+ 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapViewZoomTests.mm; sourceTree = "<group>"; };
968F36B41E4D0FC6003A5522 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
96E027241E57C76E004B8E66 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
96E027271E57C77A004B8E66 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -1173,6 +1178,8 @@
9C6E286522A9849E0056B7BE /* release-notes-jazzy.md.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = "release-notes-jazzy.md.ejs"; sourceTree = "<group>"; };
9C6E286622A9849E0056B7BE /* deploy-snapshot.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "deploy-snapshot.sh"; sourceTree = "<group>"; };
9C6E286722A9849E0056B7BE /* release-notes.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "release-notes.js"; sourceTree = "<group>"; };
+ A4DE3DC823038A07005B3473 /* MGLMockGestureRecognizers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMockGestureRecognizers.m; sourceTree = "<group>"; };
+ A4DE3DCA23038A7F005B3473 /* MGLMockGestureRecognizers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMockGestureRecognizers.h; sourceTree = "<group>"; };
A4F3FB1C2254865900A30170 /* missing_icon.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = missing_icon.json; sourceTree = "<group>"; };
AC46EB57225E60090039C013 /* MMECertPin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMECertPin.h; sourceTree = "<group>"; };
AC46EB58225E60090039C013 /* MMECertPin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMECertPin.m; sourceTree = "<group>"; };
@@ -1192,12 +1199,15 @@
CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewIntegrationTest.m; sourceTree = "<group>"; wrapsLines = 0; };
CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLMapViewIntegrationTest.h; sourceTree = "<group>"; };
CA1B4A502099FB2200EDD491 /* MGLMapSnapshotterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapSnapshotterTest.m; sourceTree = "<group>"; };
- CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCameraTransitionTests.mm; sourceTree = "<group>"; };
+ CA4C54FD2324948100A81659 /* MGLSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MGLSourceTests.swift; sourceTree = "<group>"; };
CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleLayerIntegrationTests.m; sourceTree = "<group>"; };
+ CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewPendingBlockTests.m; sourceTree = "<group>"; };
+ CA4F3BE123107793008BAFEA /* MGLCameraTransitionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCameraTransitionTests.mm; sourceTree = "<group>"; };
+ CA4F3BE3231077B9008BAFEA /* MGLCameraTransitionFinishTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCameraTransitionFinishTests.mm; sourceTree = "<group>"; };
CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = "<group>"; };
CA5E5042209BDC5F001A8A81 /* MGLTestUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MGLTestUtility.h; path = ../../darwin/test/MGLTestUtility.h; sourceTree = "<group>"; };
CA65C4F721E9BB080068B0D4 /* MGLCluster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCluster.h; sourceTree = "<group>"; };
- CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLAnnotationViewIntegrationTests.m; path = "Annotation Tests/MGLAnnotationViewIntegrationTests.m"; sourceTree = "<group>"; };
+ CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLAnnotationViewIntegrationTests.mm; path = "Annotation Tests/MGLAnnotationViewIntegrationTests.mm"; sourceTree = "<group>"; };
CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfigurationTests.m; sourceTree = "<group>"; };
CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleURLIntegrationTest.m; sourceTree = "<group>"; };
CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLRendererConfigurationTests.mm; path = ../../darwin/test/MGLRendererConfigurationTests.mm; sourceTree = "<group>"; };
@@ -1277,7 +1287,7 @@
DA618B261E68920D00CB7F44 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B2B1E68932D00CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = "<group>"; };
- DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = "<group>"; };
+ DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLVectorStyleLayer.mm; sourceTree = "<group>"; };
DA695425215B1E75002041A4 /* MGLMapCameraTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLMapCameraTests.m; path = ../../darwin/test/MGLMapCameraTests.m; sourceTree = "<group>"; };
DA704CBB1F637311004B3F28 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Foundation.strings; sourceTree = "<group>"; };
DA704CBC1F637405004B3F28 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
@@ -1504,7 +1514,7 @@
DAABF73D1CBC59BB005B1825 /* libmbgl-core.a in Frameworks */,
55D120A61F791007004B6D81 /* libmbgl-loop-darwin.a in Frameworks */,
55D120A81F79100C004B6D81 /* libmbgl-filesource.a in Frameworks */,
- 55CF752F213ED92000ED86C4 /* libicu.a in Frameworks */,
+ 55CF752F213ED92000ED86C4 /* libmbgl-vendor-icu.a in Frameworks */,
DA27C24E1CBB3811000B0ECD /* GLKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1517,7 +1527,7 @@
9680276422655696006BA4A1 /* libmbxaccounts.a in Frameworks */,
DAAE5F8920F047240089D85B /* libmbgl-filesource.a in Frameworks */,
DAAE5F8A20F0472E0089D85B /* libmbgl-loop-darwin.a in Frameworks */,
- 55CF7531213ED92A00ED86C4 /* libicu.a in Frameworks */,
+ 55CF7531213ED92A00ED86C4 /* libmbgl-vendor-icu.a in Frameworks */,
550570D22296E96E00228ECF /* GLKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1537,12 +1547,12 @@
16376B081FFD9DAF0000563E /* Integration Tests */ = {
isa = PBXGroup;
children = (
+ CA4F3BE023107793008BAFEA /* Camera Tests */,
CA6914B320E67F07002DB0EE /* Annotations */,
CAE7AD5320F46EF5003B6782 /* integration-Bridging-Header.h */,
CA1B4A4F2099FA2800EDD491 /* Snapshotter Tests */,
16376B091FFD9DAF0000563E /* MBGLIntegrationTests.mm */,
16376B0B1FFD9DAF0000563E /* Info.plist */,
- CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */,
CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */,
CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */,
CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */,
@@ -1550,6 +1560,8 @@
CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */,
077061DB215DA11F000FEF62 /* MGLTestLocationManager.h */,
077061D9215DA00E000FEF62 /* MGLTestLocationManager.m */,
+ CA4F3BDD230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m */,
+ CA4C54FD2324948100A81659 /* MGLSourceTests.swift */,
);
path = "Integration Tests";
sourceTree = "<group>";
@@ -1637,7 +1649,7 @@
353933FD1D3FB7DD003F57D7 /* MGLSymbolStyleLayer.h */,
35136D441D42275100C20EFD /* MGLSymbolStyleLayer.mm */,
DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */,
- DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */,
+ DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.mm */,
);
name = Layers;
sourceTree = "<group>";
@@ -1744,6 +1756,8 @@
children = (
CA5E5042209BDC5F001A8A81 /* MGLTestUtility.h */,
4031ACFE1E9FD29F00A3EA26 /* MGLSDKTestHelpers.swift */,
+ A4DE3DCA23038A7F005B3473 /* MGLMockGestureRecognizers.h */,
+ A4DE3DC823038A07005B3473 /* MGLMockGestureRecognizers.m */,
);
name = "Test Helpers";
sourceTree = "<group>";
@@ -1917,10 +1931,19 @@
path = "Snapshotter Tests";
sourceTree = "<group>";
};
+ CA4F3BE023107793008BAFEA /* Camera Tests */ = {
+ isa = PBXGroup;
+ children = (
+ CA4F3BE123107793008BAFEA /* MGLCameraTransitionTests.mm */,
+ CA4F3BE3231077B9008BAFEA /* MGLCameraTransitionFinishTests.mm */,
+ );
+ path = "Camera Tests";
+ sourceTree = "<group>";
+ };
CA6914B320E67F07002DB0EE /* Annotations */ = {
isa = PBXGroup;
children = (
- CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m */,
+ CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */,
);
name = Annotations;
sourceTree = "<group>";
@@ -2014,8 +2037,8 @@
isa = PBXGroup;
children = (
9680274122653C3E006BA4A1 /* libmbxaccounts.a */,
- 55CF7530213ED92A00ED86C4 /* libicu.a */,
- 55CF752E213ED92000ED86C4 /* libicu.a */,
+ 55CF7530213ED92A00ED86C4 /* libmbgl-vendor-icu.a */,
+ 55CF752E213ED92000ED86C4 /* libmbgl-vendor-icu.a */,
55D120A91F79100C004B6D81 /* libmbgl-filesource.a */,
55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */,
36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */,
@@ -2057,7 +2080,7 @@
96381C0122C6F3950053497D /* MGLMapViewPitchTests.m */,
9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */,
076171C22139C70900668A35 /* MGLMapViewTests.m */,
- 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.m */,
+ 9686D1BC22D9357700194EA0 /* MGLMapViewZoomTests.mm */,
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */,
96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */,
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */,
@@ -3188,16 +3211,19 @@
files = (
CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */,
CA7766842229C11A0008DE9E /* SMCalloutView.m in Sources */,
- CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */,
16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.mm in Sources */,
+ CA4F3BDE230F74C3008BAFEA /* MGLMapViewPendingBlockTests.m in Sources */,
CA88DC3021C85D900059ED5A /* MGLStyleURLIntegrationTest.m in Sources */,
CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */,
CA7766832229C10E0008DE9E /* MGLCompactCalloutView.m in Sources */,
CAE7AD5520F46EF5003B6782 /* MGLMapSnapshotterSwiftTests.swift in Sources */,
CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */,
077061DA215DA00E000FEF62 /* MGLTestLocationManager.m in Sources */,
- CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.m in Sources */,
+ CA6914B520E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm in Sources */,
+ CA4F3BE223107793008BAFEA /* MGLCameraTransitionTests.mm in Sources */,
+ CA4C54FE2324948100A81659 /* MGLSourceTests.swift in Sources */,
CA1B4A512099FB2200EDD491 /* MGLMapSnapshotterTest.m in Sources */,
+ CA4F3BE4231077B9008BAFEA /* MGLCameraTransitionFinishTests.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3236,6 +3262,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ A4DE3DCC23038CCA005B3473 /* MGLMockGestureRecognizers.m in Sources */,
+ A4DE3DCB23038C98005B3473 /* MGLMockGestureRecognizers.h in Sources */,
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */,
DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */,
409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */,
@@ -3273,7 +3301,7 @@
3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */,
9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m in Sources */,
409D0A0D1ED614CE00C95D0C /* MGLAnnotationViewIntegrationTests.swift in Sources */,
- 9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.m in Sources */,
+ 9686D1BD22D9357700194EA0 /* MGLMapViewZoomTests.mm in Sources */,
DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */,
07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */,
@@ -3321,7 +3349,7 @@
35136D3C1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */,
DD9BE4F81EB263C50079A3AF /* UIViewController+MGLAdditions.m in Sources */,
350098DE1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */,
- DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */,
+ DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.mm in Sources */,
40834BF71FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */,
3566C7681D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
967C864D210A9D3C004DF794 /* UIDevice+MGLAdditions.m in Sources */,
@@ -3445,7 +3473,7 @@
DD9BE4FA1EB263F40079A3AF /* UIViewController+MGLAdditions.m in Sources */,
AC46EB60225E60510039C013 /* MMEPinningConfigurationProvider.m in Sources */,
350098DF1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */,
- DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */,
+ DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.mm in Sources */,
3566C7691D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
9C6E282722A980AC0056B7BE /* UIKit+MMEMobileEvents.m in Sources */,
40834C0B1FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */,
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
index 7d59c4edee..ebe96ef82d 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -54,6 +54,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = "en"
+ region = "US"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES"
shouldUseLaunchSchemeArgsEnv = "NO">
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
index 65d4b059e8..4fdd896401 100644
--- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -40,7 +40,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ language = "en"
+ region = "US">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
@@ -54,17 +65,6 @@
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
- BuildableName = "Mapbox.framework"
- BlueprintName = "dynamic"
- ReferencedContainer = "container:ios.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -85,8 +85,6 @@
ReferencedContainer = "container:ios.xcodeproj">
</BuildableReference>
</MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
diff --git a/platform/ios/scripts/deploy-packages.sh b/platform/ios/scripts/deploy-packages.sh
index a0efbd622e..b33a25df86 100755
--- a/platform/ios/scripts/deploy-packages.sh
+++ b/platform/ios/scripts/deploy-packages.sh
@@ -7,12 +7,10 @@ set -u
# dynamic environment variables:
# VERSION_TAG={determined automatically}: Version tag in format ios-vX.X.X-pre.X
# GITHUB_RELEASE=true: Upload to github
-# BINARY_DIRECTORY=build/ios/deploy: Directory in which to save test packages
# environment variables and dependencies:
# - You must run "mbx auth ..." before running
# - Set GITHUB_TOKEN to a GitHub API access token in your environment to use GITHUB_RELEASE
-# - "wget" is required for downloading the zip files from s3
# - The "github-release" command is required to use GITHUB_RELEASE
function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; }
@@ -36,8 +34,6 @@ buildPackageStyle() {
./platform/ios/scripts/publish.sh "${PUBLISH_VERSION}" ${style}
file_name=mapbox-ios-sdk-${PUBLISH_VERSION}-${style}.zip
fi
- step "Downloading ${file_name} from s3 to ${BINARY_DIRECTORY}"
- wget -O ${BINARY_DIRECTORY}/${file_name} http://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/${file_name}
if [[ "${GITHUB_RELEASE}" == true ]]; then
step "Uploading ${file_name} to GitHub"
github-release upload \
@@ -54,7 +50,7 @@ export BUILDTYPE=Release
VERSION_TAG=${VERSION_TAG:-''}
PUBLISH_VERSION=
-BINARY_DIRECTORY=${BINARY_DIRECTORY:-build/ios/deploy}
+BINARY_DIRECTORY='build/ios'
GITHUB_RELEASE=${GITHUB_RELEASE:-true}
PUBLISH_PRE_FLAG=''
diff --git a/platform/ios/scripts/install-packaging-dependencies.sh b/platform/ios/scripts/install-packaging-dependencies.sh
index 3d87a93f20..3d7f2fa4ea 100755
--- a/platform/ios/scripts/install-packaging-dependencies.sh
+++ b/platform/ios/scripts/install-packaging-dependencies.sh
@@ -22,15 +22,6 @@ else
fi
##
-## wget
-##
-if [ -z `which wget` ]; then
- brew install wget
-else
- echo "Found brew"
-fi
-
-##
## cocoapods
##
if [[ -z `which pod` || $(pod --version) != "${COCOAPODS_VERSION}" ]]; then
diff --git a/platform/ios/scripts/publish.sh b/platform/ios/scripts/publish.sh
index b4e6f30257..1a4652a509 100755
--- a/platform/ios/scripts/publish.sh
+++ b/platform/ios/scripts/publish.sh
@@ -24,18 +24,19 @@ GITHUB_REPO=${GITHUB_REPO:-'mapbox-gl-native'}
# zip
#
cd build/ios/pkg
-SNAPSHOT_FILENAME="mapbox-ios-sdk-${PUBLISH_VERSION}${PUBLISH_STYLE}.zip"
-step "Compressing ${SNAPSHOT_FILENAME}…"
-rm -f ../${SNAPSHOT_FILENAME}
-zip -yr ../${SNAPSHOT_FILENAME} *
+ZIP_FILENAME="mapbox-ios-sdk-${PUBLISH_VERSION}${PUBLISH_STYLE}.zip"
+step "Compressing ${ZIP_FILENAME}…"
+rm -f ../${ZIP_FILENAME}
+zip -yr ../${ZIP_FILENAME} *
+cd ..
#
# report file sizes
#
step "Echoing file sizes…"
-du -sh ../${SNAPSHOT_FILENAME}
-du -sch *
-du -sch dynamic/*
+du -sh ${ZIP_FILENAME}
+du -sch pkg/*
+du -sch pkg/dynamic/*
#
# upload
@@ -45,17 +46,34 @@ if [ -n "${CI:-}" ]; then
PROGRESS="--no-progress"
fi
-step "Uploading ${SNAPSHOT_FILENAME} to s3…"
-aws s3 cp ../${SNAPSHOT_FILENAME} s3://mapbox/$GITHUB_REPO/ios/builds/ --acl public-read ${PROGRESS}
-echo "URL: https://mapbox.s3.amazonaws.com/$GITHUB_REPO/ios/builds/${SNAPSHOT_FILENAME}"
+step "Uploading ${ZIP_FILENAME} to s3…"
+aws s3 cp ${ZIP_FILENAME} s3://mapbox/${GITHUB_REPO}/ios/builds/ --acl public-read ${PROGRESS}
+S3_URL=https://mapbox.s3.amazonaws.com/${GITHUB_REPO}/ios/builds/${ZIP_FILENAME}
+echo "URL: ${S3_URL}"
#
# upload & update snapshot
#
if [[ ${PUBLISH_VERSION} =~ "snapshot" ]]; then
step "Updating ${PUBLISH_VERSION} to ${PUBLISH_STYLE}…"
- GENERIC_SNAPSHOT_FILENAME="mapbox-ios-sdk-${PUBLISH_VERSION}.zip"
+ GENERIC_ZIP_FILENAME="mapbox-ios-sdk-${PUBLISH_VERSION}.zip"
aws s3 cp \
- s3://mapbox/$GITHUB_REPO/ios/builds/${SNAPSHOT_FILENAME} \
- s3://mapbox/$GITHUB_REPO/ios/builds/${GENERIC_SNAPSHOT_FILENAME} --acl public-read ${PROGRESS}
+ s3://mapbox/$GITHUB_REPO/ios/builds/${ZIP_FILENAME} \
+ s3://mapbox/$GITHUB_REPO/ios/builds/${GENERIC_ZIP_FILENAME} --acl public-read ${PROGRESS}
+fi
+
+#
+# verify upload integrity
+#
+
+step "Validating local and remote checksums…"
+curl --output remote-${ZIP_FILENAME} ${S3_URL}
+LOCAL_CHECKSUM=$( shasum -a 256 -b ${ZIP_FILENAME} | cut -d ' ' -f 1 )
+REMOTE_CHECKSUM=$( shasum -a 256 -b remote-${ZIP_FILENAME} | cut -d ' ' -f 1 )
+
+if [ "${LOCAL_CHECKSUM}" == "${REMOTE_CHECKSUM}" ]; then
+ echo "Checksums match: ${LOCAL_CHECKSUM}"
+else
+ echo "Checksums did not match: ${LOCAL_CHECKSUM} != ${REMOTE_CHECKSUM}"
+ exit 1
fi
diff --git a/platform/ios/sdk-files.json b/platform/ios/sdk-files.json
index 4a28116550..0df4b381ba 100644
--- a/platform/ios/sdk-files.json
+++ b/platform/ios/sdk-files.json
@@ -25,7 +25,7 @@
"platform/darwin/src/MGLCircleStyleLayer.mm",
"platform/ios/src/UIViewController+MGLAdditions.m",
"platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm",
- "platform/darwin/src/MGLVectorStyleLayer.m",
+ "platform/darwin/src/MGLVectorStyleLayer.mm",
"platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEUniqueIdentifier.m",
"platform/darwin/src/MGLShapeSource.mm",
"platform/ios/src/UIDevice+MGLAdditions.m",
diff --git a/platform/ios/src/MGLCompassButton.mm b/platform/ios/src/MGLCompassButton.mm
index 94a6820a74..5bbd9bf923 100644
--- a/platform/ios/src/MGLCompassButton.mm
+++ b/platform/ios/src/MGLCompassButton.mm
@@ -1,7 +1,7 @@
#import "MGLCompassButton_Private.h"
#import "MGLCompassDirectionFormatter.h"
-#import <Mapbox/MGLGeometry.h>
+#import "MGLGeometry.h"
#import "MGLMapView_Private.h"
#import "UIImage+MGLAdditions.h"
@@ -63,9 +63,16 @@
UIImage *scaleImage = [UIImage mgl_resourceImageNamed:@"Compass"];
UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, UIScreen.mainScreen.scale);
[scaleImage drawInRect:{CGPointZero, scaleImage.size}];
+
+ UIFont *northFont;
+ if (@available(iOS 13.0, *)) {
+ northFont = [UIFont systemFontOfSize:11 weight:UIFontWeightLight];
+ } else {
+ northFont = [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight];
+ }
NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{
- NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight],
+ NSFontAttributeName: northFont,
NSForegroundColorAttributeName: [UIColor whiteColor],
}];
CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2,
diff --git a/platform/ios/src/MGLCompassButton_Private.h b/platform/ios/src/MGLCompassButton_Private.h
index c9741d79e3..9ef55cfa5b 100644
--- a/platform/ios/src/MGLCompassButton_Private.h
+++ b/platform/ios/src/MGLCompassButton_Private.h
@@ -1,6 +1,6 @@
#import <UIKit/UIKit.h>
-#import <Mapbox/MGLCompassButton.h>
+#import "MGLCompassButton.h"
@class MGLMapView;
diff --git a/platform/ios/src/MGLMapView+Impl.h b/platform/ios/src/MGLMapView+Impl.h
index 0a62b7da82..232215bd1b 100644
--- a/platform/ios/src/MGLMapView+Impl.h
+++ b/platform/ios/src/MGLMapView+Impl.h
@@ -62,7 +62,7 @@ public:
void onDidFinishLoadingMap() override;
void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override;
void onWillStartRenderingFrame() override;
- void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) override;
+ void onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus) override;
void onWillStartRenderingMap() override;
void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) override;
void onDidFinishLoadingStyle() override;
diff --git a/platform/ios/src/MGLMapView+Impl.mm b/platform/ios/src/MGLMapView+Impl.mm
index 1bccfa662f..9cdab4dc63 100644
--- a/platform/ios/src/MGLMapView+Impl.mm
+++ b/platform/ios/src/MGLMapView+Impl.mm
@@ -68,8 +68,8 @@ void MGLMapViewImpl::onWillStartRenderingFrame() {
[mapView mapViewWillStartRenderingFrame];
}
-void MGLMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) {
- bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full;
+void MGLMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus status) {
+ bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered];
}
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index f799852fa7..017ba525c4 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -289,9 +289,16 @@ MGL_EXPORT
/**
A Boolean value indicating whether the map may display scale information.
- The scale bar may not be shown at all zoom levels. The view controlled by this
- property is available at `scaleBar`. The default value of this property is
- `NO`.
+ The scale bar may not be shown at all zoom levels. The scale bar becomes visible
+ when the maximum distance visible on the map view is less than 400 miles (800
+ kilometers). The zoom level where this occurs depends on the latitude at the map
+ view’s center coordinate, as well as the device screen width. At latitudes
+ farther from the equator, the scale bar becomes visible at lower zoom levels.
+
+ The unit of measurement is determined by the user's device locale.
+
+ The view controlled by this property is available at `scaleBar`. The default value
+ of this property is `NO`.
*/
@property (nonatomic, assign) BOOL showsScale;
@@ -741,7 +748,8 @@ MGL_EXPORT
Changes the center coordinate of the map and optionally animates the change.
Changing the center coordinate centers the map on the new coordinate without
- changing the current zoom level.
+ changing the current zoom level. For animated changes, wait until the map view has
+ finished loading before calling this method.
@param coordinate The new center coordinate for the map.
@param animated Specify `YES` if you want the map view to scroll to the new
@@ -755,7 +763,8 @@ MGL_EXPORT
/**
Changes the center coordinate and zoom level of the map and optionally animates
- the change.
+ the change. For animated changes, wait until the map view has
+ finished loading before calling this method.
@param centerCoordinate The new center coordinate for the map.
@param zoomLevel The new zoom level for the map.
@@ -770,7 +779,8 @@ MGL_EXPORT
/**
Changes the center coordinate, zoom level, and direction of the map and
- optionally animates the change.
+ optionally animates the change. For animated changes, wait until the map view has
+ finished loading before calling this method.
@param centerCoordinate The new center coordinate for the map.
@param zoomLevel The new zoom level for the map.
@@ -787,7 +797,8 @@ MGL_EXPORT
/**
Changes the center coordinate, zoom level, and direction of the map, calling a
- completion handler at the end of an optional animation.
+ completion handler at the end of an optional animation. For animated changes,
+ wait until the map view has finished loading before calling this method.
@param centerCoordinate The new center coordinate for the map.
@param zoomLevel The new zoom level for the map.
@@ -1058,7 +1069,8 @@ MGL_EXPORT
/**
Moves the viewpoint to a different location with respect to the map with an
- optional transition animation.
+ optional transition animation. For animated changes, wait until the map view has
+ finished loading before calling this method.
@param camera The new viewpoint.
@param animated Specify `YES` if you want the map view to animate the change to
@@ -1074,7 +1086,8 @@ MGL_EXPORT
/**
Moves the viewpoint to a different location with respect to the map with an
- optional transition duration and timing function.
+ optional transition duration and timing function. For animated changes, wait
+ until the map view has finished loading before calling this method.
@param camera The new viewpoint.
@param duration The amount of time, measured in seconds, that the transition
@@ -1093,7 +1106,8 @@ MGL_EXPORT
/**
Moves the viewpoint to a different location with respect to the map with an
- optional transition duration and timing function.
+ optional transition duration and timing function. For animated changes, wait
+ until the map view has finished loading before calling this method.
@param camera The new viewpoint.
@param duration The amount of time, measured in seconds, that the transition
@@ -1109,7 +1123,8 @@ MGL_EXPORT
/**
Moves the viewpoint to a different location with respect to the map with an
optional transition duration and timing function, and optionally some additional
- padding on each side.
+ padding on each side. For animated changes, wait until the map view has
+ finished loading before calling this method.
@param camera The new viewpoint.
@param duration The amount of time, measured in seconds, that the transition
@@ -1299,8 +1314,8 @@ MGL_EXPORT
property may be overridden at any time.
Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the `-setContentInset:animated:` method
- instead.
+ want to animate the change, use the `-setContentInset:animated:completionHandler:`
+ method instead.
*/
@property (nonatomic, assign) UIEdgeInsets contentInset;
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index d77f94d8ba..62b943fd3d 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -128,6 +128,9 @@ const CLLocationDirection MGLToleranceForSnappingToNorth = 7;
/// Distance threshold to stop the camera while animating.
const CLLocationDistance MGLDistanceThresholdForCameraPause = 500;
+/// Rotation threshold while a pinch gesture is occurring.
+static NSString * const MGLRotationThresholdWhileZoomingKey = @"MGLRotationThresholdWhileZooming";
+
/// Reuse identifier and file name of the default point annotation image.
static NSString * const MGLDefaultStyleMarkerSymbolName = @"default_marker";
@@ -154,6 +157,9 @@ static const NSUInteger MGLPresentsWithTransactionAnnotationCount = 0;
/// An indication that the requested annotation was not found or is nonexistent.
enum { MGLAnnotationTagNotFound = UINT32_MAX };
+/// The threshold used to consider when a tilt gesture should start.
+const CLLocationDegrees MGLHorizontalTiltToleranceDegrees = 45.0;
+
/// Mapping from an annotation tag to metadata about that annotation, including
/// the annotation itself.
typedef std::unordered_map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotationTagContextMap;
@@ -240,6 +246,10 @@ public:
@property (nonatomic) CGFloat quickZoomStart;
@property (nonatomic, getter=isDormant) BOOL dormant;
@property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed;
+@property (nonatomic) CGFloat rotationThresholdWhileZooming;
+@property (nonatomic) CGFloat rotationBeforeThresholdMet;
+@property (nonatomic) BOOL isZooming;
+@property (nonatomic) BOOL isRotating;
@property (nonatomic) BOOL shouldTriggerHapticFeedbackForCompass;
@property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement;
@property (nonatomic) MGLAnnotationContainerView *annotationContainerView;
@@ -247,6 +257,7 @@ public:
@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier;
@property (nonatomic, readonly) BOOL enablePresentsWithTransaction;
@property (nonatomic) UIImage *lastSnapshotImage;
+@property (nonatomic) NSMutableArray *pendingCompletionBlocks;
/// Experimental rendering performance measurement.
@property (nonatomic) BOOL experimental_enableFrameRateMeasurement;
@@ -260,6 +271,9 @@ public:
@property (nonatomic) MGLMapDebugMaskOptions residualDebugMask;
@property (nonatomic, copy) NSURL *residualStyleURL;
+/// Tilt gesture recognizer helper
+@property (nonatomic, assign) CGPoint dragGestureMiddlePoint;
+
- (mbgl::Map &)mbglMap;
@end
@@ -457,7 +471,7 @@ public:
// setup mbgl map
MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration];
- auto renderer = std::make_unique<mbgl::Renderer>(_mbglView->getRendererBackend(), config.scaleFactor, config.cacheDir, config.localFontFamilyName);
+ auto renderer = std::make_unique<mbgl::Renderer>(_mbglView->getRendererBackend(), config.scaleFactor, config.localFontFamilyName);
BOOL enableCrossSourceCollisions = !config.perSourceCollisions;
_rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, _mbglView->getRendererBackend());
@@ -569,6 +583,7 @@ public:
_rotate.delegate = self;
[self addGestureRecognizer:_rotate];
_rotateEnabled = YES;
+ _rotationThresholdWhileZooming = 3;
_doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTapGesture:)];
_doubleTap.numberOfTapsRequired = 2;
@@ -612,6 +627,11 @@ public:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
+
+ // Pending completion blocks are called *after* annotation views have been updated
+ // in updateFromDisplayLink.
+ _pendingCompletionBlocks = [NSMutableArray array];
+
// As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `pauseRendering:` in response to it, as doing
// so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225
@@ -1033,6 +1053,34 @@ public:
return CGPointMake(CGRectGetMidX(contentFrame), CGRectGetMidY(contentFrame));
}
+#pragma mark - Pending completion blocks
+
+- (void)processPendingBlocks
+{
+ NSArray *blocks = self.pendingCompletionBlocks;
+ self.pendingCompletionBlocks = [NSMutableArray array];
+
+ for (dispatch_block_t block in blocks)
+ {
+ block();
+ }
+}
+
+- (BOOL)scheduleTransitionCompletion:(dispatch_block_t)block
+{
+ // Only add a block if the display link (that calls processPendingBlocks) is
+ // running, otherwise fall back to calling immediately.
+ if (_displayLink && !_displayLink.isPaused)
+ {
+ [self willChangeValueForKey:@"pendingCompletionBlocks"];
+ [self.pendingCompletionBlocks addObject:block];
+ [self didChangeValueForKey:@"pendingCompletionBlocks"];
+ return YES;
+ }
+
+ return NO;
+}
+
#pragma mark - Life Cycle -
- (void)updateFromDisplayLink:(CADisplayLink *)displayLink
@@ -1057,7 +1105,7 @@ public:
return;
}
- if (_needsDisplayRefresh)
+ if (_needsDisplayRefresh || (self.pendingCompletionBlocks.count > 0))
{
_needsDisplayRefresh = NO;
@@ -1066,6 +1114,13 @@ public:
[self updateAnnotationViews];
[self updateCalloutView];
+ // Call any pending completion blocks. This is primarily to ensure
+ // that annotations are in the expected position after core rendering
+ // and map update.
+ //
+ // TODO: Consider using this same mechanism for delegate callbacks.
+ [self processPendingBlocks];
+
_mbglView->display();
}
@@ -1132,6 +1187,7 @@ public:
{
[_displayLink invalidate];
_displayLink = nil;
+ [self processPendingBlocks];
}
}
@@ -1415,6 +1471,7 @@ public:
[MGLMapboxEvents flush];
_displayLink.paused = YES;
+ [self processPendingBlocks];
if ( ! self.glSnapshotView)
{
@@ -1467,6 +1524,11 @@ public:
{
super.hidden = hidden;
_displayLink.paused = hidden;
+
+ if (hidden)
+ {
+ [self processPendingBlocks];
+ }
}
- (void)tintColorDidChange
@@ -1622,6 +1684,9 @@ public:
{
self.scale = powf(2, [self zoomLevel]);
+ if (abs(pinch.velocity) > abs(self.rotate.velocity)) {
+ self.isZooming = YES;
+ }
[self notifyGestureDidBegin];
}
else if (pinch.state == UIGestureRecognizerStateChanged)
@@ -1697,6 +1762,7 @@ public:
}
}
+ self.isZooming = NO;
[self notifyGestureDidEndWithDrift:drift];
[self unrotateIfNeededForGesture];
}
@@ -1716,20 +1782,30 @@ public:
self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureRotate;
- if (rotate.state == UIGestureRecognizerStateBegan)
+ if ([[NSUserDefaults standardUserDefaults] objectForKey:MGLRotationThresholdWhileZoomingKey]) {
+ self.rotationThresholdWhileZooming = [[[NSUserDefaults standardUserDefaults] objectForKey:MGLRotationThresholdWhileZoomingKey] floatValue];
+ }
+ // Check whether a zoom triggered by a pinch gesture is occurring and if the rotation threshold has been met.
+ if (MGLDegreesFromRadians(self.rotationBeforeThresholdMet) < self.rotationThresholdWhileZooming && self.isZooming && !self.isRotating) {
+ self.rotationBeforeThresholdMet += fabs(rotate.rotation);
+ rotate.rotation = 0;
+ return;
+ }
+
+ if (rotate.state == UIGestureRecognizerStateBegan || ! self.isRotating)
{
self.angle = MGLRadiansFromDegrees(*self.mbglMap.getCameraOptions().bearing) * -1;
+ self.isRotating = YES;
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
self.userTrackingMode = MGLUserTrackingModeFollow;
}
self.shouldTriggerHapticFeedbackForCompass = NO;
-
[self notifyGestureDidBegin];
}
- else if (rotate.state == UIGestureRecognizerStateChanged)
+ if (rotate.state == UIGestureRecognizerStateChanged)
{
CGFloat newDegrees = MGLDegreesFromRadians(self.angle + rotate.rotation) * -1;
@@ -1740,14 +1816,14 @@ public:
newDegrees = fminf(newDegrees, 30);
newDegrees = fmaxf(newDegrees, -30);
}
-
+
MGLMapCamera *toCamera = [self cameraByRotatingToDirection:newDegrees aroundAnchorPoint:centerPoint];
if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- self.mbglMap.jumpTo(mbgl::CameraOptions()
- .withBearing(newDegrees)
- .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y}));
+ self.mbglMap.jumpTo(mbgl::CameraOptions()
+ .withBearing(newDegrees)
+ .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y}));
}
[self cameraIsChanging];
@@ -1768,8 +1844,12 @@ public:
}
}
}
- else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
+ else if ((rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled))
{
+ self.rotationBeforeThresholdMet = 0;
+ if (! self.isRotating) { return; }
+ self.isRotating = NO;
+
CGFloat velocity = rotate.velocity;
CGFloat decelerationRate = self.decelerationRate;
if (decelerationRate != MGLMapViewDecelerationRateImmediate && fabs(velocity) > 3)
@@ -1783,14 +1863,13 @@ public:
if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
self.mbglMap.easeTo(mbgl::CameraOptions()
- .withBearing(newDegrees)
- .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }),
+ .withBearing(newDegrees)
+ .withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }),
MGLDurationFromTimeInterval(decelerationRate));
[self notifyGestureDidEndWithDrift:YES];
-
__weak MGLMapView *weakSelf = self;
-
+
[self animateWithDelay:decelerationRate animations:^
{
[weakSelf unrotateIfNeededForGesture];
@@ -2016,6 +2095,14 @@ public:
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
+ CGPoint midPoint = [twoFingerDrag translationInView:twoFingerDrag.view];
+ // In the following if and for the first execution middlePoint
+ // will be equal to dragGestureMiddlePoint and the resulting
+ // gestureSlopeAngle will be 0º causing a small delay,
+ // initializing dragGestureMiddlePoint with the current midPoint
+ // but substracting one point from 'y' forces an initial 90º angle
+ // making the gesture avoid the delay
+ self.dragGestureMiddlePoint = CGPointMake(midPoint.x, midPoint.y-1);
initialPitch = *self.mbglMap.getCameraOptions().pitch;
[self notifyGestureDidBegin];
}
@@ -2027,30 +2114,45 @@ public:
twoFingerDrag.state = UIGestureRecognizerStateEnded;
return;
}
-
- CGFloat gestureDistance = CGPoint([twoFingerDrag translationInView:twoFingerDrag.view]).y;
- CGFloat slowdown = 2.0;
-
- CGFloat pitchNew = initialPitch - (gestureDistance / slowdown);
-
- CGPoint centerPoint = [self anchorPointForGesture:twoFingerDrag];
-
- MGLMapCamera *oldCamera = self.camera;
- MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew];
-
- if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
- {
- self.mbglMap.jumpTo(mbgl::CameraOptions()
+
+ CGPoint leftTouchPoint = [twoFingerDrag locationOfTouch:0 inView:twoFingerDrag.view];
+ CGPoint rightTouchPoint = [twoFingerDrag locationOfTouch:1 inView:twoFingerDrag.view];
+ CLLocationDegrees fingerSlopeAngle = [self angleBetweenPoints:leftTouchPoint endPoint:rightTouchPoint];
+
+ CGPoint middlePoint = [twoFingerDrag translationInView:twoFingerDrag.view];
+
+ CLLocationDegrees gestureSlopeAngle = [self angleBetweenPoints:self.dragGestureMiddlePoint endPoint:middlePoint];
+ self.dragGestureMiddlePoint = middlePoint;
+ if (fabs(fingerSlopeAngle) < MGLHorizontalTiltToleranceDegrees && fabs(gestureSlopeAngle) > 60.0 ) {
+
+ CGFloat gestureDistance = middlePoint.y;
+ CGFloat slowdown = 2.0;
+
+ CGFloat pitchNew = initialPitch - (gestureDistance / slowdown);
+
+ CGPoint centerPoint = [self anchorPointForGesture:twoFingerDrag];
+
+ MGLMapCamera *oldCamera = self.camera;
+ MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew];
+
+ if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
+ {
+ self.mbglMap.jumpTo(mbgl::CameraOptions()
.withPitch(pitchNew)
.withAnchor(mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }));
+ }
+
+ [self cameraIsChanging];
+
}
- [self cameraIsChanging];
+
}
else if (twoFingerDrag.state == UIGestureRecognizerStateEnded || twoFingerDrag.state == UIGestureRecognizerStateCancelled)
{
[self notifyGestureDidEndWithDrift:NO];
[self unrotateIfNeededForGesture];
+ self.dragGestureMiddlePoint = CGPointZero;
}
}
@@ -2173,23 +2275,17 @@ public:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
- if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
+ if (gestureRecognizer == _twoFingerDrag)
{
UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
if (panGesture.minimumNumberOfTouches == 2)
{
- CGPoint west = [panGesture locationOfTouch:0 inView:panGesture.view];
- CGPoint east = [panGesture locationOfTouch:1 inView:panGesture.view];
-
- if (west.x > east.x) {
- CGPoint swap = west;
- west = east;
- east = swap;
- }
+ CGPoint leftTouchPoint = [panGesture locationOfTouch:0 inView:panGesture.view];
+ CGPoint rightTouchPoint = [panGesture locationOfTouch:1 inView:panGesture.view];
- CLLocationDegrees horizontalToleranceDegrees = 60.0;
- if ([self angleBetweenPoints:west east:east] > horizontalToleranceDegrees) {
+ CLLocationDegrees degrees = [self angleBetweenPoints:leftTouchPoint endPoint:rightTouchPoint];
+ if (fabs(degrees) > MGLHorizontalTiltToleranceDegrees) {
return NO;
}
}
@@ -2211,18 +2307,24 @@ public:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSArray *validSimultaneousGestures = @[ self.pan, self.pinch, self.rotate ];
-
return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]);
}
-- (CLLocationDegrees)angleBetweenPoints:(CGPoint)west east:(CGPoint)east
+- (CLLocationDegrees)angleBetweenPoints:(CGPoint)originPoint endPoint:(CGPoint)endPoint
{
- CGFloat slope = (west.y - east.y) / (west.x - east.x);
+ if (originPoint.x > endPoint.x) {
+ CGPoint swap = originPoint;
+ originPoint = endPoint;
+ endPoint = swap;
+ }
+
+ CGFloat x = (endPoint.x - originPoint.x);
+ CGFloat y = (endPoint.y - originPoint.y);
- CGFloat angle = atan(fabs(slope));
- CLLocationDegrees degrees = MGLDegreesFromRadians(angle);
+ CGFloat angleInRadians = atan2(y, x);
+ CLLocationDegrees angleInDegrees = MGLDegreesFromRadians(angleInRadians);
- return degrees;
+ return angleInDegrees;
}
#pragma mark - Attribution -
@@ -2302,7 +2404,7 @@ public:
NSString *message;
NSString *participateTitle;
NSString *declineTitle;
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"])
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabledKey])
{
message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_MSG", nil, nil, @"You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message");
participateTitle = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_ON", nil, nil, @"Keep Participating", @"Telemetry prompt button");
@@ -2330,14 +2432,14 @@ public:
UIAlertAction *declineAction = [UIAlertAction actionWithTitle:declineTitle
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
- [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"MGLMapboxMetricsEnabled"];
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:MGLMapboxMetricsEnabledKey];
}];
[alertController addAction:declineAction];
UIAlertAction *participateAction = [UIAlertAction actionWithTitle:participateTitle
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"MGLMapboxMetricsEnabled"];
+ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:MGLMapboxMetricsEnabledKey];
}];
[alertController addAction:participateAction];
@@ -3188,26 +3290,35 @@ public:
animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
+
+ dispatch_block_t pendingCompletion;
+
if (completion)
{
- animationOptions.transitionFinishFn = [completion]() {
+ __weak __typeof__(self) weakSelf = self;
+
+ pendingCompletion = ^{
+ if (![weakSelf scheduleTransitionCompletion:completion])
+ {
+ completion();
+ }
+ };
+
+ animationOptions.transitionFinishFn = [pendingCompletion]() {
// Must run asynchronously after the transition is completely over.
// Otherwise, a call to -setCenterCoordinate: within the completion
// handler would reenter the completion handler’s caller.
- dispatch_async(dispatch_get_main_queue(), ^{
- completion();
- });
+
+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
};
}
MGLMapCamera *camera = [self cameraForCameraOptions:cameraOptions];
if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, insets))
{
- if (completion)
+ if (pendingCompletion)
{
- [self animateWithDelay:duration animations:^{
- completion();
- }];
+ [self animateWithDelay:duration animations:pendingCompletion];
}
return;
}
@@ -3374,12 +3485,22 @@ public:
animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
+
+ dispatch_block_t pendingCompletion;
+
if (completion)
{
- animationOptions.transitionFinishFn = [completion]() {
- dispatch_async(dispatch_get_main_queue(), ^{
+ __weak __typeof__(self) weakSelf = self;
+
+ pendingCompletion = ^{
+ if (![weakSelf scheduleTransitionCompletion:completion])
+ {
completion();
- });
+ }
+ };
+
+ animationOptions.transitionFinishFn = [pendingCompletion]() {
+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
};
}
@@ -3389,11 +3510,9 @@ public:
MGLMapCamera *camera = [self cameraForCameraOptions:cameraOptions];
if ([self.camera isEqualToMapCamera:camera])
{
- if (completion)
+ if (pendingCompletion)
{
- [self animateWithDelay:duration animations:^{
- completion();
- }];
+ [self animateWithDelay:duration animations:pendingCompletion];
}
return;
}
@@ -3534,22 +3653,30 @@ public:
animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
+
+ dispatch_block_t pendingCompletion;
+
if (completion)
{
- animationOptions.transitionFinishFn = [completion]() {
- dispatch_async(dispatch_get_main_queue(), ^{
+ __weak __typeof__(self) weakSelf = self;
+
+ pendingCompletion = ^{
+ if (![weakSelf scheduleTransitionCompletion:completion])
+ {
completion();
- });
+ }
+ };
+
+ animationOptions.transitionFinishFn = [pendingCompletion]() {
+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
};
}
if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, edgePadding))
{
- if (completion)
+ if (pendingCompletion)
{
- [self animateWithDelay:duration animations:^{
- completion();
- }];
+ [self animateWithDelay:duration animations:pendingCompletion];
}
return;
}
@@ -3605,22 +3732,30 @@ public:
animationOptions.minZoom = MGLZoomLevelForAltitude(peakAltitude, peakPitch,
peakLatitude, self.frame.size);
}
+
+ dispatch_block_t pendingCompletion;
+
if (completion)
{
- animationOptions.transitionFinishFn = [completion]() {
- dispatch_async(dispatch_get_main_queue(), ^{
+ __weak __typeof__(self) weakSelf = self;
+
+ pendingCompletion = ^{
+ if (![weakSelf scheduleTransitionCompletion:completion])
+ {
completion();
- });
+ }
+ };
+
+ animationOptions.transitionFinishFn = [pendingCompletion]() {
+ dispatch_async(dispatch_get_main_queue(), pendingCompletion);
};
}
if ([self.camera isEqualToMapCamera:camera] && UIEdgeInsetsEqualToEdgeInsets(_contentInset, insets))
{
- if (completion)
+ if (pendingCompletion)
{
- [self animateWithDelay:duration animations:^{
- completion();
- }];
+ [self animateWithDelay:duration animations:pendingCompletion];
}
return;
}
@@ -5168,35 +5303,29 @@ public:
if (shouldEnableLocationServices)
{
- if (self.locationManager.authorizationStatus == kCLAuthorizationStatusNotDetermined)
- {
- BOOL requiresWhenInUseUsageDescription = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){11,0,0}];
+ if (self.locationManager.authorizationStatus == kCLAuthorizationStatusNotDetermined) {
BOOL hasWhenInUseUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"];
- BOOL hasAlwaysUsageDescription;
- if (requiresWhenInUseUsageDescription)
- {
- hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"] && hasWhenInUseUsageDescription;
- }
- else
- {
- hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"];
- }
- if (hasAlwaysUsageDescription)
- {
- [self.locationManager requestAlwaysAuthorization];
- }
- else if (hasWhenInUseUsageDescription)
- {
- [self.locationManager requestWhenInUseAuthorization];
- }
- else
- {
- NSString *suggestedUsageKeys = requiresWhenInUseUsageDescription ?
- @"NSLocationWhenInUseUsageDescription and (optionally) NSLocationAlwaysAndWhenInUseUsageDescription" :
- @"NSLocationWhenInUseUsageDescription and/or NSLocationAlwaysUsageDescription";
- [NSException raise:MGLMissingLocationServicesUsageDescriptionException
- format:@"This app must have a value for %@ in its Info.plist.", suggestedUsageKeys];
+ if (@available(iOS 11.0, *)) {
+ // A WhenInUse string is required in iOS 11+ and the map never has any need for Always, so it's enough to just ask for WhenInUse.
+ if (hasWhenInUseUsageDescription) {
+ [self.locationManager requestWhenInUseAuthorization];
+ } else {
+ [NSException raise:MGLMissingLocationServicesUsageDescriptionException
+ format:@"To use location services this app must have a NSLocationWhenInUseUsageDescription string in its Info.plist."];
+ }
+ } else {
+ // We might have to ask for Always if the app does not provide a WhenInUse string.
+ BOOL hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"];
+
+ if (hasWhenInUseUsageDescription) {
+ [self.locationManager requestWhenInUseAuthorization];
+ } else if (hasAlwaysUsageDescription) {
+ [self.locationManager requestAlwaysAuthorization];
+ } else {
+ [NSException raise:MGLMissingLocationServicesUsageDescriptionException
+ format:@"To use location services this app must have a NSLocationWhenInUseUsageDescription and/or NSLocationAlwaysUsageDescription string in its Info.plist."];
+ }
}
}
diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h
index e53dc8519c..155527000f 100644
--- a/platform/ios/src/MGLMapView_Private.h
+++ b/platform/ios/src/MGLMapView_Private.h
@@ -52,8 +52,6 @@ FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const _Nonnull MGLUnderlyingMapUna
/// Synchronously render a frame of the map.
- (void)renderSync;
-- (nonnull mbgl::Map *)mbglMap;
-
- (nonnull mbgl::Renderer *)renderer;
/** Returns whether the map view is currently loading or processing any assets required to render the map */
diff --git a/platform/ios/src/MGLMapboxEvents.h b/platform/ios/src/MGLMapboxEvents.h
index cb3132656f..a7d316cc06 100644
--- a/platform/ios/src/MGLMapboxEvents.h
+++ b/platform/ios/src/MGLMapboxEvents.h
@@ -3,6 +3,9 @@
NS_ASSUME_NONNULL_BEGIN
+/// NSUserDefaults key that controls telemetry user opt-out status
+FOUNDATION_EXTERN NSString * const MGLMapboxMetricsEnabledKey;
+
@interface MGLMapboxEvents : NSObject
+ (nullable instancetype)sharedInstance;
diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m
index cc7390ac61..808c3a88bf 100644
--- a/platform/ios/src/MGLMapboxEvents.m
+++ b/platform/ios/src/MGLMapboxEvents.m
@@ -3,14 +3,16 @@
#import "NSBundle+MGLAdditions.h"
#import "MGLAccountManager_Private.h"
+// NSUserDefaults and Info.plist keys
+NSString * const MGLMapboxMetricsEnabledKey = @"MGLMapboxMetricsEnabled";
+static NSString * const MGLMapboxMetricsDebugLoggingEnabledKey = @"MGLMapboxMetricsDebugLoggingEnabled";
+static NSString * const MGLMapboxMetricsEnabledSettingShownInAppKey = @"MGLMapboxMetricsEnabledSettingShownInApp";
+static NSString * const MGLTelemetryAccessTokenKey = @"MGLTelemetryAccessToken";
+static NSString * const MGLTelemetryBaseURLKey = @"MGLTelemetryBaseURL";
+static NSString * const MGLEventsProfileKey = @"MMEEventsProfile";
+static NSString * const MGLVariableGeofenceKey = @"VariableGeofence";
+
static NSString * const MGLAPIClientUserAgentBase = @"mapbox-maps-ios";
-static NSString * const MGLMapboxAccountType = @"MGLMapboxAccountType";
-static NSString * const MGLMapboxMetricsEnabled = @"MGLMapboxMetricsEnabled";
-static NSString * const MGLMapboxMetricsDebugLoggingEnabled = @"MGLMapboxMetricsDebugLoggingEnabled";
-static NSString * const MGLTelemetryAccessToken = @"MGLTelemetryAccessToken";
-static NSString * const MGLTelemetryBaseURL = @"MGLTelemetryBaseURL";
-static NSString * const MGLEventsProfile = @"MMEEventsProfile";
-static NSString * const MGLVariableGeofence = @"VariableGeofence";
@interface MGLMapboxEvents ()
@@ -19,16 +21,16 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
@property (nonatomic, copy) NSString *accessToken;
@end
-@implementation MGLMapboxEvents
+@implementation MGLMapboxEvents
+ (void)initialize {
if (self == [MGLMapboxEvents class]) {
NSBundle *bundle = [NSBundle mainBundle];
- NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:MGLMapboxAccountType];
- [[NSUserDefaults standardUserDefaults] registerDefaults:@{MGLMapboxAccountType: accountTypeNumber ?: @0,
- MGLMapboxMetricsEnabled: @YES,
- MGLMapboxMetricsDebugLoggingEnabled: @NO}];
+ NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:MGLMapboxAccountTypeKey];
+ [[NSUserDefaults standardUserDefaults] registerDefaults:@{MGLMapboxAccountTypeKey: accountTypeNumber ?: @0,
+ MGLMapboxMetricsEnabledKey: @YES,
+ MGLMapboxMetricsDebugLoggingEnabledKey: @NO}];
}
}
@@ -46,9 +48,9 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
self = [super init];
if (self) {
_eventsManager = MMEEventsManager.sharedManager;
- _eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabled];
- _eventsManager.accountType = [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType];
- _eventsManager.metricsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabled];
+ _eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabledKey];
+ _eventsManager.accountType = [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountTypeKey];
+ _eventsManager.metricsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabledKey];
// It is possible for the shared instance of this class to be created because of a call to
// +[MGLAccountManager load] early on in the app lifecycle of the host application.
@@ -57,11 +59,11 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
// (once -[MMEEventsManager initializeWithAccessToken:userAgentBase:hostSDKVersion:] is called.
// Normally, the telem access token and base URL are not set this way. However, overriding these values
// with user defaults can be useful for testing with an alternative (test) backend system.
- if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessToken]) {
- self.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken];
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessTokenKey]) {
+ self.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessTokenKey];
}
- if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) {
- self.baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]];
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURLKey]) {
+ self.baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURLKey]];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil];
@@ -81,22 +83,22 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
}
- (void)updateNonDisablingConfigurationValues {
- self.eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
+ self.eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabledKey];
- // It is possible for `MGLTelemetryAccessToken` to have been set yet `userDefaultsDidChange:`
+ // It is possible for the telemetry access token key to have been set yet `userDefaultsDidChange:`
// is called before `setupWithAccessToken:` is called.
// In that case, setting the access token here will have no effect. In practice, that's fine
// because the access token value will be resolved when `setupWithAccessToken:` is called eventually
- if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessToken]) {
- self.eventsManager.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken];
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessTokenKey]) {
+ self.eventsManager.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessTokenKey];
}
- // It is possible for `MGLTelemetryBaseURL` to have been set yet `userDefaultsDidChange:`
+ // It is possible for the telemetry base URL key to have been set yet `userDefaultsDidChange:`
// is called before setupWithAccessToken: is called.
// In that case, setting the base URL here will have no effect. In practice, that's fine
// because the base URL value will be resolved when `setupWithAccessToken:` is called eventually
- if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) {
- NSURL *baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]];
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURLKey]) {
+ NSURL *baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURLKey]];
self.eventsManager.baseURL = baseURL;
}
}
@@ -109,8 +111,8 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
if ([[notification object] respondsToSelector:@selector(objectForKey:)]) {
NSUserDefaults *userDefaults = [notification object];
- NSInteger accountType = [userDefaults integerForKey:MGLMapboxAccountType];
- BOOL metricsEnabled = [userDefaults boolForKey:MGLMapboxMetricsEnabled];
+ NSInteger accountType = [userDefaults integerForKey:MGLMapboxAccountTypeKey];
+ BOOL metricsEnabled = [userDefaults boolForKey:MGLMapboxMetricsEnabledKey];
if (accountType != self.eventsManager.accountType || metricsEnabled != self.eventsManager.metricsEnabled) {
self.eventsManager.accountType = accountType;
@@ -124,7 +126,7 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
+ (void)setupWithAccessToken:(NSString *)accessToken {
int64_t delayTime = 0;
- if ([[[NSBundle mainBundle] objectForInfoDictionaryKey:MGLEventsProfile] isEqualToString:MGLVariableGeofence]) {
+ if ([[[NSBundle mainBundle] objectForInfoDictionaryKey:MGLEventsProfileKey] isEqualToString:MGLVariableGeofenceKey]) {
delayTime = 10;
}
@@ -164,11 +166,11 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
}
+ (void)ensureMetricsOptoutExists {
- NSNumber *shownInAppNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"];
+ NSNumber *shownInAppNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:MGLMapboxMetricsEnabledSettingShownInAppKey];
BOOL metricsEnabledSettingShownInAppFlag = [shownInAppNumber boolValue];
if (!metricsEnabledSettingShownInAppFlag &&
- [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType] == 0) {
+ [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountTypeKey] == 0) {
// Opt-out is not configured in UI, so check for Settings.bundle
id defaultEnabledValue;
NSString *appSettingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
@@ -178,7 +180,7 @@ static NSString * const MGLVariableGeofence = @"VariableGeofence";
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[appSettingsBundle stringByAppendingPathComponent:@"Root.plist"]];
NSArray *preferences = settings[@"PreferenceSpecifiers"];
for (NSDictionary *prefSpecification in preferences) {
- if ([prefSpecification[@"Key"] isEqualToString:MGLMapboxMetricsEnabled]) {
+ if ([prefSpecification[@"Key"] isEqualToString:MGLMapboxMetricsEnabledKey]) {
defaultEnabledValue = prefSpecification[@"DefaultValue"];
}
}
diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm
index 9590a99438..8525881da7 100644
--- a/platform/ios/src/MGLScaleBar.mm
+++ b/platform/ios/src/MGLScaleBar.mm
@@ -1,4 +1,4 @@
-#import <Mapbox/Mapbox.h>
+#import "Mapbox.h"
#import "MGLScaleBar.h"
static const CGFloat MGLFeetPerMile = 5280;
@@ -84,7 +84,7 @@ static const MGLRow MGLImperialTable[] ={
@property (nonatomic) UIColor *secondaryColor;
@property (nonatomic) CALayer *borderLayer;
@property (nonatomic, assign) CGFloat borderWidth;
-@property (nonatomic) NSCache* labelImageCache;
+@property (nonatomic) NSMutableDictionary* labelImageCache;
@property (nonatomic) MGLScaleBarLabel* prototypeLabel;
@property (nonatomic) CGFloat lastLabelWidth;
@@ -101,7 +101,6 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
- (void)drawTextInRect:(CGRect)rect {
CGSize shadowOffset = self.shadowOffset;
- UIColor *textColor = self.textColor;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
@@ -112,7 +111,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
[super drawTextInRect:rect];
CGContextSetTextDrawingMode(context, kCGTextFill);
- self.textColor = textColor;
+ self.textColor = [UIColor blackColor];
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
@@ -159,7 +158,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
_formatter = [[MGLDistanceFormatter alloc] init];
// Image labels are now images
- _labelImageCache = [[NSCache alloc] init];
+ _labelImageCache = [[NSMutableDictionary alloc] init];
_prototypeLabel = [[MGLScaleBarLabel alloc] init];
_prototypeLabel.font = [UIFont systemFontOfSize:8 weight:UIFontWeightMedium];
_prototypeLabel.clipsToBounds = NO;
@@ -180,6 +179,17 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
// Zero is a special case (no formatting)
[self addZeroLabel];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetLabelImageCache) name:NSCurrentLocaleDidChangeNotification object:nil];
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)resetLabelImageCache {
+ self.labelImageCache = [[NSMutableDictionary alloc] init];
+ [self addZeroLabel];
}
#pragma mark - Dimensions
diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h
index 55835c50d6..23fac9dbeb 100644
--- a/platform/ios/src/UIImage+MGLAdditions.h
+++ b/platform/ios/src/UIImage+MGLAdditions.h
@@ -1,6 +1,6 @@
#import <UIKit/UIKit.h>
-#import <Mapbox/MGLTypes.h>
+#import "MGLTypes.h"
#include <mbgl/style/image.hpp>
diff --git a/platform/ios/test/MGLMapAccessibilityElementTests.m b/platform/ios/test/MGLMapAccessibilityElementTests.m
index 2312d1d406..916461e708 100644
--- a/platform/ios/test/MGLMapAccessibilityElementTests.m
+++ b/platform/ios/test/MGLMapAccessibilityElementTests.m
@@ -8,11 +8,6 @@
@implementation MGLMapAccessibilityElementTests
-- (void)setUp {
- // FIXME: https://github.com/mapbox/mapbox-gl-native/issues/14908
- XCTAssertEqualObjects(NSLocale.currentLocale.localeIdentifier, @"en_US", @"Device locale must be en_US for these tests to pass.");
-}
-
- (void)testFeatureLabels {
MGLPointFeature *feature = [[MGLPointFeature alloc] init];
feature.attributes = @{
diff --git a/platform/ios/test/MGLMapViewDirectionTests.mm b/platform/ios/test/MGLMapViewDirectionTests.mm
index 8a724a06bc..81e169432b 100644
--- a/platform/ios/test/MGLMapViewDirectionTests.mm
+++ b/platform/ios/test/MGLMapViewDirectionTests.mm
@@ -1,5 +1,6 @@
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>
+#import "MGLMockGestureRecognizers.h"
#import <mbgl/math/wrap.hpp>
@@ -8,12 +9,6 @@
- (void)resetNorthAnimated:(BOOL)animated;
@end
-@interface UIRotationGestureRecognizerMock : UIRotationGestureRecognizer
-@end
-
-@implementation UIRotationGestureRecognizerMock
-- (CGPoint)locationInView:(nullable UIView*)view { return view.center; }
-@end
@interface MGLMapViewDirectionTests : XCTestCase
@property (nonatomic) MGLMapView *mapView;
diff --git a/platform/ios/test/MGLMapViewPitchTests.m b/platform/ios/test/MGLMapViewPitchTests.m
index b7b18973a1..3e9311dbd4 100644
--- a/platform/ios/test/MGLMapViewPitchTests.m
+++ b/platform/ios/test/MGLMapViewPitchTests.m
@@ -2,23 +2,35 @@
#import <XCTest/XCTest.h>
@interface MockUIPanGestureRecognizer : UIPanGestureRecognizer
-@property CGFloat mbx_tiltGestureYTranslation;
@property NSUInteger mbx_numberOfFingersForGesture;
+@property CGPoint mbx_middlePoint;
+@property CGPoint mbx_westPoint;
+@property CGPoint mbx_eastPoint;
@end
@implementation MockUIPanGestureRecognizer
- (instancetype)initWithTarget:(id)target action:(SEL)action {
if (self = [super initWithTarget:target action:action]) {
- self.mbx_tiltGestureYTranslation = 0;
self.mbx_numberOfFingersForGesture = 2;
+ self.mbx_westPoint = CGPointMake(100, 0);
+ self.mbx_eastPoint = CGPointMake(200, 0);
}
return self;
}
- (NSUInteger)numberOfTouches { return self.mbx_numberOfFingersForGesture; }
-- (CGPoint)translationInView:(UIView *)view { return CGPointMake(0, self.mbx_tiltGestureYTranslation); }
+- (CGPoint)translationInView:(UIView *)view { return self.mbx_middlePoint; }
+- (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(UIView *)view {
+ if (touchIndex == 0) {
+ return self.mbx_westPoint;
+ }
+ return self.mbx_eastPoint;
+}
- (void)setTiltGestureYTranslationForPitchDegrees:(CGFloat)degrees {
// The tilt gesture takes the number of screen points the fingers have moved and then divides them by a "slowdown" factor, which happens to be set to 2.0 in -[MGLMapView handleTwoFingerDragGesture:].
- self.mbx_tiltGestureYTranslation = -(degrees * 2.0);
+ CGFloat mbx_tiltGestureYTranslation = -(degrees * 2.0);
+ self.mbx_westPoint = CGPointMake(self.mbx_westPoint.x, mbx_tiltGestureYTranslation);
+ self.mbx_eastPoint = CGPointMake(self.mbx_eastPoint.x, mbx_tiltGestureYTranslation);
+ self.mbx_middlePoint = CGPointMake(self.mbx_middlePoint.x, mbx_tiltGestureYTranslation);
}
@end
@@ -112,6 +124,25 @@
}
}
+- (void)testHorizontalTiltGesture {
+ MockUIPanGestureRecognizer *gesture = [[MockUIPanGestureRecognizer alloc] initWithTarget:self.mapView action:nil];
+ gesture.state = UIGestureRecognizerStateBegan;
+ [self.mapView handleTwoFingerDragGesture:gesture];
+ XCTAssertEqual(self.mapView.camera.pitch, 0, @"Pitch should initially be set to 0°.");
+
+ // Tilt gestures should not be triggered on horizontal dragging.
+ for (NSInteger deltaX = 0; deltaX < 5; deltaX++) {
+ gesture.mbx_westPoint = CGPointMake(100 - deltaX, 100);
+ gesture.mbx_eastPoint = CGPointMake(100 - deltaX, 50);
+ gesture.mbx_middlePoint = CGPointMake((gesture.mbx_westPoint.x + gesture.mbx_westPoint.x)/2, (gesture.mbx_westPoint.y + gesture.mbx_westPoint.y)/2);
+
+ gesture.state = UIGestureRecognizerStateChanged;
+
+ [self.mapView handleTwoFingerDragGesture:gesture];
+ XCTAssertEqual(self.mapView.camera.pitch, 0, @"Horizontal dragging should not tilt the map.");
+ }
+}
+
- (void)testTiltGestureFromInitialTilt {
CGFloat initialTilt = 20;
CGFloat additionalTilt = 30;
diff --git a/platform/ios/test/MGLMapViewZoomTests.m b/platform/ios/test/MGLMapViewZoomTests.mm
index bd617857fd..360af72d02 100644
--- a/platform/ios/test/MGLMapViewZoomTests.m
+++ b/platform/ios/test/MGLMapViewZoomTests.mm
@@ -1,16 +1,14 @@
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>
+#import "MGLMockGestureRecognizers.h"
+
+#import <mbgl/math/wrap.hpp>
@interface MGLMapView (MGLMapViewZoomTests)
+@property (nonatomic) BOOL isZooming;
+@property (nonatomic) CGFloat rotationThresholdWhileZooming;
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch;
-@end
-
-@interface UIPinchGestureRecognizerMock : UIPinchGestureRecognizer
-@property (nonatomic) CGPoint locationInViewOverride;
-@end
-
-@implementation UIPinchGestureRecognizerMock
-- (CGPoint)locationInView:(nullable UIView *)view { return self.locationInViewOverride; }
+- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate;
@end
@interface MGLMapViewZoomTests : XCTestCase
@@ -129,6 +127,60 @@
XCTAssertEqualWithAccuracy(centerCoordinateBeforeReset.longitude, self.mapView.centerCoordinate.longitude, 0.0000001, @"Map center coordinate longitude should remain constant after resetting to north.");
}
+- (void)testPinchAndZoom {
+
+ [[NSUserDefaults standardUserDefaults] setObject:@3 forKey:@"MGLRotationThresholdWhileZooming"];
+ self.mapView.rotationThresholdWhileZooming = 3;
+ self.mapView.zoomLevel = 15;
+ UIPinchGestureRecognizerMock *pinch = [[UIPinchGestureRecognizerMock alloc] initWithTarget:self.mapView action:nil];
+ [self.mapView addGestureRecognizer:pinch];
+ pinch.state = UIGestureRecognizerStateBegan;
+ pinch.velocity = 5.0;
+ pinch.locationInViewOverride = CGPointMake(0, 0);
+ [self.mapView handlePinchGesture:pinch];
+
+ XCTAssertTrue(self.mapView.isZooming);
+
+ UIRotationGestureRecognizerMock *rotate = [[UIRotationGestureRecognizerMock alloc] initWithTarget:self.mapView action:nil];
+ rotate.state = UIGestureRecognizerStateBegan;
+ rotate.rotation = MGLRadiansFromDegrees(1);
+ [self.mapView addGestureRecognizer:rotate];
+ [self.mapView handleRotateGesture:rotate];
+
+ // Both the rotation and direction should be zero since the rotation threshold hasn't been met.
+ XCTAssertEqual(rotate.rotation, 0);
+ XCTAssertEqual(self.mapView.direction, 0);
+
+ // The direction should be `0`. The default rotation threshold is `3`.
+ XCTAssertEqual(self.mapView.direction, 0);
+ rotate.state = UIGestureRecognizerStateChanged;
+ rotate.rotation = MGLRadiansFromDegrees(2);
+ [self.mapView handleRotateGesture:rotate];
+
+ // The direction should be `0`. The default rotation threshold is `3`.
+ XCTAssertEqual(self.mapView.direction, 0);
+
+ for (NSNumber *degrees in @[@-90, @-10, @10, @10, @30, @90, @180, @240, @460, @500, @590, @800]) {
+ rotate.state = UIGestureRecognizerStateChanged;
+ rotate.rotation = MGLRadiansFromDegrees([degrees doubleValue]);
+ [self.mapView handleRotateGesture:rotate];
+
+ CGFloat wrappedRotation = mbgl::util::wrap(-MGLDegreesFromRadians(rotate.rotation), 0., 360.);
+
+
+ // Check that the direction property now matches the gesture's rotation.
+ XCTAssertEqualWithAccuracy(self.mapView.direction, wrappedRotation, 0.001, @"Map direction should match gesture rotation for input of %@°.", degrees);
+ }
+
+ rotate.state = UIGestureRecognizerStateEnded;
+ pinch.state = UIGestureRecognizerStateEnded;
+
+ [self.mapView handleRotateGesture:rotate];
+ [self.mapView handlePinchGesture:pinch];
+
+ XCTAssertFalse(self.mapView.isZooming);
+}
+
NS_INLINE CGFloat MGLScaleFromZoomLevel(double zoom) {
return pow(2, zoom);
}
diff --git a/platform/ios/test/MGLMockGestureRecognizers.h b/platform/ios/test/MGLMockGestureRecognizers.h
new file mode 100644
index 0000000000..aa5fbec494
--- /dev/null
+++ b/platform/ios/test/MGLMockGestureRecognizers.h
@@ -0,0 +1,10 @@
+
+#import <UIKit/UIKit.h>
+
+@interface UIPinchGestureRecognizerMock : UIPinchGestureRecognizer
+@property (nonatomic, readwrite) CGFloat velocity;
+@property (nonatomic) CGPoint locationInViewOverride;
+@end
+
+@interface UIRotationGestureRecognizerMock : UIRotationGestureRecognizer
+@end
diff --git a/platform/ios/test/MGLMockGestureRecognizers.m b/platform/ios/test/MGLMockGestureRecognizers.m
new file mode 100644
index 0000000000..89df6750a9
--- /dev/null
+++ b/platform/ios/test/MGLMockGestureRecognizers.m
@@ -0,0 +1,11 @@
+
+#import "MGLMockGestureRecognizers.h"
+
+@implementation UIPinchGestureRecognizerMock
+@synthesize velocity;
+- (CGPoint)locationInView:(nullable UIView *)view { return self.locationInViewOverride; }
+@end
+
+@implementation UIRotationGestureRecognizerMock
+- (CGPoint)locationInView:(nullable UIView*)view { return view.center; }
+@end
diff --git a/platform/ios/vendor/mapbox-accounts-ios/libmbxaccounts.a b/platform/ios/vendor/mapbox-accounts-ios/libmbxaccounts.a
index 5cecc4c790..30d46a4f31 100644
--- a/platform/ios/vendor/mapbox-accounts-ios/libmbxaccounts.a
+++ b/platform/ios/vendor/mapbox-accounts-ios/libmbxaccounts.a
Binary files differ
diff --git a/platform/linux/README.md b/platform/linux/README.md
index e1a231dd28..4dc951f12e 100644
--- a/platform/linux/README.md
+++ b/platform/linux/README.md
@@ -66,10 +66,10 @@ Set an access token as described below, and then run:
### Test
-- `make test-*` Builds and runs all tests. You can specify individual tests by replacing * with their name (e.g. `make test-Sprite.CustomSpriteImages`).
+- `make run-test-*` Builds and runs all tests. You can specify individual tests by replacing * with their name (e.g. `make run-test-Sprite.CustomSpriteImages`).
The `zsh` will treat the * in this command as a glob, so you'll need to run
-`make "test-*"` instead.
+`make "run-test-*"` instead.
### Usage
diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake
index aa65ddb606..74860ea0d7 100644
--- a/platform/linux/config.cmake
+++ b/platform/linux/config.cmake
@@ -98,17 +98,9 @@ macro(mbgl_platform_core)
PRIVATE ${LIBICUI18N}
PRIVATE ${LIBICUUC}
PRIVATE ${LIBICUDATA}
- PRIVATE nunicode
+ PRIVATE mbgl-vendor-nunicode
PUBLIC -lz
)
-
- if(WITH_CXX11ABI)
- # Statically link libstdc++ when we're using the new STL ABI
- target_link_libraries(mbgl-core
- PUBLIC -static-libstdc++
- PUBLIC -Wl,-Bsymbolic-functions
- )
- endif()
endmacro()
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 6e32ad5c8b..888d4c5267 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -2,6 +2,7 @@
## master
+* Fixed crashes triggered when `MGLSource` and `MGLStyleLayer` objects are accessed after having been invalidated after a style change. ([#15539](https://github.com/mapbox/mapbox-gl-native/pull/15539))
* Fixed an issue where it was possible to set the map’s content insets then tilt the map enough to see the horizon, causing performance issues. ([#15195](https://github.com/mapbox/mapbox-gl-native/pull/15195))
* Added an `MGLMapView.prefetchesTiles` property to configure lower-resolution tile prefetching behavior. ([#14816](https://github.com/mapbox/mapbox-gl-native/pull/14816))
* Fixed queryRenderedFeatues bug caused by incorrect sort feature index calculation. ([#14884](https://github.com/mapbox/mapbox-gl-native/pull/14884))
@@ -9,12 +10,21 @@
* The `MGLIdeographicFontFamilyName` Info.plist key now also accepts an array of font family names, to customize font fallback behavior. It can also be set to a Boolean value of `NO` to force the SDK to typeset CJK characters in a remote font specified by `MGLSymbolStyleLayer.textFontNames`. ([#14862](https://github.com/mapbox/mapbox-gl-native/pull/14862))
* Performance improvements for queryRenderedFeatures API and optimization that allocates containers based on a number of rendered layers. ([#14930](https://github.com/mapbox/mapbox-gl-native/pull/14930))
* Fixed rendering layers after fill-extrusion regression caused by optimization of fill-extrusion rendering. ([#15065](https://github.com/mapbox/mapbox-gl-native/pull/15065))
-* `MGLLoggingLevel` has been updated for better matching core log levels. Now can use `[MGLLoggingConfiguration sharedConfiguration].loggingLevel` to filter logs from core . [#15120](https://github.com/mapbox/mapbox-gl-native/pull/15120)
+* `MGLLoggingLevel` has been updated for better matching core log levels. Now can use `[MGLLoggingConfiguration sharedConfiguration].loggingLevel` to filter logs from core . ([#15120](https://github.com/mapbox/mapbox-gl-native/pull/15120))
+* Fixed an issue where animated camera transitions zoomed in or out too dramatically. ([#15281](https://github.com/mapbox/mapbox-gl-native/pull/15281))
+* Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer. ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367))
+* Fixed symbol overlap when zooming out quickly. ([15416](https://github.com/mapbox/mapbox-gl-native/pull/15416))
+* Fixed a rendering issue that non-SDF icon would be treated as SDF icon if they are in the same layer. ([#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456))
+* Fixed a rendering issue of `collisionBox` when `text-translate` or `icon-translate` is enabled. ([#15467](https://github.com/mapbox/mapbox-gl-native/pull/15467))
+* Fixed an issue of integer overflow when converting `tileCoordinates` to `LatLon`, which caused issues such as `queryRenderedFeatures` and `querySourceFeatures` returning incorrect coordinates at zoom levels 20 and higher. ([#15560](https://github.com/mapbox/mapbox-gl-native/pull/15560))
+* Added an `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]` method to provide the snapshot's current `CGContext` in order to perform custom drawing on `MGLMapSnapShot` objects. ([#15530](https://github.com/mapbox/mapbox-gl-native/pull/15530))
+* Fixed an issue that `maxzoom` in style `Sources` option was ignored when URL resource is provided. It may cause problems such as extra tiles downloading at higher zoom level than `maxzoom`, or problems that wrong setting of `overscaledZ` in `OverscaledTileID` that will be passed to `SymbolLayout`, leading wrong rendering appearance. ([#15581](https://github.com/mapbox/mapbox-gl-native/pull/15581))
### Styles and rendering
* Setting `MGLMapView.contentInset` now moves the map’s focal point to the center of the content frame after insetting. ([#14664](https://github.com/mapbox/mapbox-gl-native/pull/14664))
* Added the `-[MGLMapViewDelegate mapView:shouldRemoveStyleImage:]` method for optimizing style image caching. ([#14769](https://github.com/mapbox/mapbox-gl-native/pull/14769))
+* Introduce `text-writing-mode` layout property for symbol layer ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)). The `text-writing-mode` layout property allows control over symbol's preferred writing mode. The new property value is an array, whose values are enumeration values from a ( `horizontal` | `vertical` ) set.
### Other changes
diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md
index 5a81eb3593..399ba6804b 100644
--- a/platform/macos/docs/guides/For Style Authors.md
+++ b/platform/macos/docs/guides/For Style Authors.md
@@ -257,6 +257,7 @@ In style JSON | In Objective-C | In Swift
`text-justify` | `MGLSymbolStyleLayer.textJustification` | `MGLSymbolStyleLayer.textJustification`
`text-optional` | `MGLSymbolStyleLayer.textOptional` | `MGLSymbolStyleLayer.isTextOptional`
`text-rotate` | `MGLSymbolStyleLayer.textRotation` | `MGLSymbolStyleLayer.textRotation`
+`text-writing-mode` | `MGLSymbolStyleLayer.textWritingModes` | `MGLSymbolStyleLayer.textWritingModes`
`icon-translate` | `MGLSymbolStyleLayer.iconTranslation` | `MGLSymbolStyleLayer.iconTranslation`
`icon-translate-anchor` | `MGLSymbolStyleLayer.iconTranslationAnchor` | `MGLSymbolStyleLayer.iconTranslationAnchor`
`text-translate` | `MGLSymbolStyleLayer.textTranslation` | `MGLSymbolStyleLayer.textTranslation`
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 45e5a6d653..50f592a4bc 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -98,7 +98,7 @@
5591AC6B2298361600FF9ADF /* MGLMapView+Impl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5591AC692298361600FF9ADF /* MGLMapView+Impl.mm */; };
55CAF6322294407F00F17770 /* MGLMapView+OpenGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 55CAF6312294407F00F17770 /* MGLMapView+OpenGL.h */; };
55CAF6342294409B00F17770 /* MGLMapView+OpenGL.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55CAF6332294409B00F17770 /* MGLMapView+OpenGL.mm */; };
- 55CF7533213EDADF00ED86C4 /* libicu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CF7532213EDADF00ED86C4 /* libicu.a */; };
+ 55CF7533213EDADF00ED86C4 /* libmbgl-vendor-icu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55CF7532213EDADF00ED86C4 /* libmbgl-vendor-icu.a */; };
55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */; };
@@ -156,7 +156,7 @@
DA5589771D320C41006B7F64 /* wms.json in Resources */ = {isa = PBXBuildFile; fileRef = DA5589761D320C41006B7F64 /* wms.json */; };
DA57D4B11EBC699800793288 /* MGLDocumentationGuideTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA57D4B01EBC699800793288 /* MGLDocumentationGuideTests.swift */; };
DA6408D71DA4E5DA00908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D51DA4E5DA00908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DA6408D81DA4E5DA00908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.m */; };
+ DA6408D81DA4E5DA00908C90 /* MGLVectorStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.mm */; };
DA695424215B1E6C002041A4 /* MGLMapCameraTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA695423215B1E6C002041A4 /* MGLMapCameraTests.m */; };
DA7262071DEEDD460043BB89 /* MGLOpenGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7262051DEEDD460043BB89 /* MGLOpenGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA7262081DEEDD460043BB89 /* MGLOpenGLStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA7262061DEEDD460043BB89 /* MGLOpenGLStyleLayer.mm */; };
@@ -424,7 +424,7 @@
5591AC692298361600FF9ADF /* MGLMapView+Impl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+Impl.mm"; sourceTree = "<group>"; };
55CAF6312294407F00F17770 /* MGLMapView+OpenGL.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+OpenGL.h"; sourceTree = "<group>"; };
55CAF6332294409B00F17770 /* MGLMapView+OpenGL.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+OpenGL.mm"; sourceTree = "<group>"; };
- 55CF7532213EDADF00ED86C4 /* libicu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libicu.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55CF7532213EDADF00ED86C4 /* libmbgl-vendor-icu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libmbgl-vendor-icu.a; sourceTree = BUILT_PRODUCTS_DIR; };
55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
55D9B4B01D005D3900C1CCE2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = "<group>"; };
@@ -516,7 +516,7 @@
DA618B271E68926E00CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B2A1E6892B500CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
DA6408D51DA4E5DA00908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = "<group>"; };
- DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = "<group>"; };
+ DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLVectorStyleLayer.mm; sourceTree = "<group>"; };
DA695423215B1E6C002041A4 /* MGLMapCameraTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLMapCameraTests.m; path = ../../darwin/test/MGLMapCameraTests.m; sourceTree = "<group>"; };
DA704CBA1F6372E8004B3F28 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Foundation.strings; sourceTree = "<group>"; };
DA704CBE1F637531004B3F28 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -740,7 +740,7 @@
5548BE781D09E718005DDE81 /* libmbgl-core.a in Frameworks */,
55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */,
52B5D17F1E5E26DF00BBCB48 /* libmbgl-loop-darwin.a in Frameworks */,
- 55CF7533213EDADF00ED86C4 /* libicu.a in Frameworks */,
+ 55CF7533213EDADF00ED86C4 /* libmbgl-vendor-icu.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -799,7 +799,7 @@
DA8F25911D51CA750010E6B5 /* MGLSymbolStyleLayer.h */,
DA8F25921D51CA750010E6B5 /* MGLSymbolStyleLayer.mm */,
DA6408D51DA4E5DA00908C90 /* MGLVectorStyleLayer.h */,
- DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.m */,
+ DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.mm */,
);
name = Layers;
sourceTree = "<group>";
@@ -1146,7 +1146,7 @@
DAE6C31E1CC308BC00DB3429 /* Frameworks */ = {
isa = PBXGroup;
children = (
- 55CF7532213EDADF00ED86C4 /* libicu.a */,
+ 55CF7532213EDADF00ED86C4 /* libmbgl-vendor-icu.a */,
55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */,
5548BE7B1D0ACBBD005DDE81 /* libmbgl-loop-darwin.a */,
55D9B4B01D005D3900C1CCE2 /* libz.tbd */,
@@ -1695,7 +1695,7 @@
DA35A2A61CC9EB2700E826B2 /* MGLCoordinateFormatter.m in Sources */,
352742821D4C243B00A1ECE6 /* MGLSource.mm in Sources */,
DAE6C3881CC31E2A00DB3429 /* MGLMapCamera.mm in Sources */,
- DA6408D81DA4E5DA00908C90 /* MGLVectorStyleLayer.m in Sources */,
+ DA6408D81DA4E5DA00908C90 /* MGLVectorStyleLayer.mm in Sources */,
1FF4858D2237235300F19727 /* MGLAttributedExpression.m in Sources */,
DA8F25B31D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */,
DAE6C3911CC31E2A00DB3429 /* MGLPolygon.mm in Sources */,
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
index 5f1cf773ae..cfb1eca382 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -56,7 +56,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "9EEBD19ABEE247CF8D3CF4C5"
+ BlueprintIdentifier = "9771D7F76DA54F52A89268C6"
BuildableName = "mbgl-test"
BlueprintName = "mbgl-test"
ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj">
@@ -70,7 +70,7 @@
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "B328AB16ECE141AAAE2A72C8"
+ BlueprintIdentifier = "D87DA13F4A80489CB4508EBD"
BuildableName = "mbgl-benchmark"
BlueprintName = "mbgl-benchmark"
ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj">
@@ -82,7 +82,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ language = "en"
+ region = "US">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "macosapp"
+ ReferencedContainer = "container:macos.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
@@ -95,17 +106,6 @@
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
- BuildableName = "Mapbox GL.app"
- BlueprintName = "macosapp"
- ReferencedContainer = "container:macos.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -126,8 +126,6 @@
ReferencedContainer = "container:macos.xcodeproj">
</BuildableReference>
</MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
index fc623b22fa..55a36344d5 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -40,7 +40,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ language = "en"
+ region = "US">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3271CC30DB200DB3429"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:macos.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
@@ -53,17 +64,6 @@
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "DAE6C3271CC30DB200DB3429"
- BuildableName = "Mapbox.framework"
- BlueprintName = "dynamic"
- ReferencedContainer = "container:macos.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -84,8 +84,6 @@
ReferencedContainer = "container:macos.xcodeproj">
</BuildableReference>
</MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
index ce62359388..98e6c54a33 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
@@ -26,8 +26,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES"
language = "en"
- shouldUseLaunchSchemeArgsEnv = "YES">
+ region = "US">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "macosapp"
+ ReferencedContainer = "container:macos.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
@@ -40,17 +50,6 @@
</BuildableReference>
</TestableReference>
</Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
- BuildableName = "Mapbox GL.app"
- BlueprintName = "macosapp"
- ReferencedContainer = "container:macos.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- <AdditionalOptions>
- </AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@@ -72,8 +71,6 @@
ReferencedContainer = "container:macos.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
- <AdditionalOptions>
- </AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
diff --git a/platform/macos/sdk-files.json b/platform/macos/sdk-files.json
index 022f679b4a..13760da70f 100644
--- a/platform/macos/sdk-files.json
+++ b/platform/macos/sdk-files.json
@@ -49,7 +49,7 @@
"platform/darwin/src/MGLCoordinateFormatter.m",
"platform/darwin/src/MGLSource.mm",
"platform/darwin/src/MGLMapCamera.mm",
- "platform/darwin/src/MGLVectorStyleLayer.m",
+ "platform/darwin/src/MGLVectorStyleLayer.mm",
"platform/darwin/src/MGLAttributedExpression.m",
"platform/darwin/src/NSValue+MGLStyleAttributeAdditions.mm",
"platform/darwin/src/MGLPolygon.mm",
diff --git a/platform/macos/src/MGLAnnotationImage_Private.h b/platform/macos/src/MGLAnnotationImage_Private.h
index 21963a86a0..428f1db5d9 100644
--- a/platform/macos/src/MGLAnnotationImage_Private.h
+++ b/platform/macos/src/MGLAnnotationImage_Private.h
@@ -1,4 +1,4 @@
-#import <Mapbox/Mapbox.h>
+#import "Mapbox.h"
@interface MGLAnnotationImage (Private)
diff --git a/platform/macos/src/MGLMapView+Impl.h b/platform/macos/src/MGLMapView+Impl.h
index 2d523716d4..d33a19dbab 100644
--- a/platform/macos/src/MGLMapView+Impl.h
+++ b/platform/macos/src/MGLMapView+Impl.h
@@ -30,7 +30,7 @@ public:
void onDidFinishLoadingMap() override;
void onDidFailLoadingMap(mbgl::MapLoadError mapError, const std::string& what) override;
void onWillStartRenderingFrame() override;
- void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) override;
+ void onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus) override;
void onWillStartRenderingMap() override;
void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) override;
void onDidFinishLoadingStyle() override;
diff --git a/platform/macos/src/MGLMapView+Impl.mm b/platform/macos/src/MGLMapView+Impl.mm
index 2354f67a6d..c00f858153 100644
--- a/platform/macos/src/MGLMapView+Impl.mm
+++ b/platform/macos/src/MGLMapView+Impl.mm
@@ -67,8 +67,8 @@ void MGLMapViewImpl::onWillStartRenderingFrame() {
[mapView mapViewWillStartRenderingFrame];
}
-void MGLMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) {
- bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full;
+void MGLMapViewImpl::onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus status) {
+ bool fullyRendered = status.mode == mbgl::MapObserver::RenderMode::Full;
[mapView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered];
}
diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h
index 7617063430..374d4eeab7 100644
--- a/platform/macos/src/MGLMapView.h
+++ b/platform/macos/src/MGLMapView.h
@@ -620,8 +620,8 @@ MGL_EXPORT IB_DESIGNABLE
the value of this property may be overridden at any time.
Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the `-setContentInsets:animated:` method
- instead.
+ want to animate the change, use the `-setContentInset:animated:completionHandler:`
+ method instead.
*/
@property (nonatomic, assign) NSEdgeInsets contentInsets;
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 3c9647571e..9a7cb437ec 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -276,7 +276,7 @@ public:
MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration];
- auto renderer = std::make_unique<mbgl::Renderer>(_mbglView->getRendererBackend(), config.scaleFactor, config.cacheDir, config.localFontFamilyName);
+ auto renderer = std::make_unique<mbgl::Renderer>(_mbglView->getRendererBackend(), config.scaleFactor, config.localFontFamilyName);
BOOL enableCrossSourceCollisions = !config.perSourceCollisions;
_rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, _mbglView->getRendererBackend(), true);
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md
index 2999a70f05..486dd44e10 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,11 +1,20 @@
+# 4.3.0
+* Introduce `text-writing-mode` layout property for symbol layer ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)). The `text-writing-mode` layout property allows control over symbol's preferred writing mode. The new property value is an array, whose values are enumeration values from a ( `horizontal` | `vertical` ) set.
+* Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)).
+* Fixed a rendering issue that non-SDF icon would be treated as SDF icon if they are in the same layer. ([#15456](https://github.com/mapbox/mapbox-gl-native/pull/15456))
+* Fixed a rendering issue of `collisionBox` when `text-translate` or `icon-translate` is enabled. ([#15467](https://github.com/mapbox/mapbox-gl-native/pull/15467))
+* Fixed an issue of integer overflow when converting `tileCoordinates` to `LatLon`, which caused issues such as `queryRenderedFeatures` and `querySourceFeatures` returning incorrect coordinates at zoom levels 20 and higher. ([#15560](https://github.com/mapbox/mapbox-gl-native/pull/15560))
+* Add typechecking while constructing legacy filter to prevent converting an unexpected filter type [#15389](https://github.com/mapbox/mapbox-gl-native/pull/15389).
+* Fixed an issue that `maxzoom` in style `Sources` option was ignored when URL resource is provided. It may cause problems such as extra tiles downloading at higher zoom level than `maxzoom`, or problems that wrong setting of `overscaledZ` in `OverscaledTileID` that will be passed to `SymbolLayout`, leading wrong rendering appearance. ([#15581](https://github.com/mapbox/mapbox-gl-native/pull/15581))
+
# 4.2.0
- Add an option to set whether or not an image should be treated as a SDF ([#15054](https://github.com/mapbox/mapbox-gl-native/issues/15054))
-- Fix problems associated with node 10 and NAN [#14847](https://github.com/mapbox/mapbox-gl-native/pull/14847)
+- Fix problems associated with node 10 and NAN ([#14847](https://github.com/mapbox/mapbox-gl-native/pull/14847))
# 4.1.0
-- Add `symbol-z-order` symbol layout property to style spec [#12783](https://github.com/mapbox/mapbox-gl-native/pull/12783)
-- Add `crossSourceCollisions` map option, with default of `true`. When set to `false`, cross-source collision detection is disabled. ([#12820] (https://github.com/mapbox/mapbox-gl-native/issues/12820))
-- Fixed bugs in coercion expression operators ("to-array" applied to empty arrays, "to-color" applied to colors, and "to-number" applied to null) [#12864](https://github.com/mapbox/mapbox-gl-native/pull/12864)
+- Add `symbol-z-order` symbol layout property to style spec ([#12783](https://github.com/mapbox/mapbox-gl-native/pull/12783))
+- Add `crossSourceCollisions` map option, with default of `true`. When set to `false`, cross-source collision detection is disabled. ([#12820](https://github.com/mapbox/mapbox-gl-native/issues/12820))
+- Fixed bugs in coercion expression operators ("to-array" applied to empty arrays, "to-color" applied to colors, and "to-number" applied to null) ([#12864](https://github.com/mapbox/mapbox-gl-native/pull/12864))
- Fixed an issue where fill and line layers would occasionally flicker on zoom ([#12982](https://github.com/mapbox/mapbox-gl-native/pull/12982))
# 4.0.0
diff --git a/platform/node/DEVELOPING.md b/platform/node/DEVELOPING.md
index 066448c08e..3d07253ee3 100644
--- a/platform/node/DEVELOPING.md
+++ b/platform/node/DEVELOPING.md
@@ -43,7 +43,7 @@ To publish a new version of the package:
- [ ] an updated version number in [`package.json`](../../package.json#L3)
- [ ] an entry in [`platform/node/CHANGELOG.md`](CHANGELOG.md) describing the changes in the release
- [ ] run `git tag node-v{VERSION}` where `{VERSION}` matches the version in `package.json`, e.g. `git tag node-v3.3.2`
-- [ ] run `git push && git push --gs`
+- [ ] run `git push && git push --tags`
The CI builds for tag pushes will check if the tag matches the version listed in `package.json`, and if so, will run with `BUILDTYPE=Release` and publish a binary with `node-pre-gyp`.
diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp
index 041f60d029..e1c3ba0c39 100644
--- a/platform/node/src/node_expression.cpp
+++ b/platform/node/src/node_expression.cpp
@@ -192,6 +192,11 @@ struct ToValue {
} else {
serializedSection.emplace("fontStack", mbgl::NullValue());
}
+ if (section.textColor) {
+ serializedSection.emplace("textColor", section.textColor->toObject());
+ } else {
+ serializedSection.emplace("textColor", mbgl::NullValue());
+ }
sections.emplace_back(serializedSection);
}
serialized.emplace("sections", sections);
diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json
index 4e4f537a23..d32fb6b0c4 100644
--- a/platform/node/test/ignores.json
+++ b/platform/node/test/ignores.json
@@ -39,13 +39,11 @@
"render-tests/custom-layer-js/null-island": "skip - js specific",
"render-tests/custom-layer-js/tent-3d": "skip - js specific",
"render-tests/regressions/mapbox-gl-js#7708": "skip - js specific",
+ "render-tests/debug/overdraw": "https://github.com/mapbox/mapbox-gl-native/issues/15638",
"render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
- "render-tests/debug/collision-lines": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
- "render-tests/debug/collision-lines-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
- "render-tests/debug/collision-lines-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
- "render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
"render-tests/debug/tile": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
"render-tests/debug/tile-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/raster": "https://github.com/mapbox/mapbox-gl-native/issues/15510",
"render-tests/extent/1024-circle": "needs investigation",
"render-tests/fill-extrusion-pattern/@2x": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/function": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
@@ -54,12 +52,8 @@
"render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/feature-expression": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/tile-buffer": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
- "render-tests/fill-pattern/literal": "FLAKY: do not re-enable without extensive testing - https://github.com/mapbox/mapbox-gl-native/issues/14423",
- "render-tests/fill-pattern/opacity": "FLAKY: do not re-enable without extensive testing - https://github.com/mapbox/mapbox-gl-native/issues/14870",
"render-tests/fill-pattern/update-feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue",
- "render-tests/fill-pattern/zoomed": "FLAKY: do not re-enable without extensive testing - https://github.com/mapbox/mapbox-gl-native/issues/14768",
"render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary",
- "render-tests/line-translate/literal": "FLAKY: do not re-enable without extensive testing - https://github.com/mapbox/mapbox-gl-native/issues/14859",
"render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397",
"render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195",
"render-tests/real-world/bangkok": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
@@ -68,7 +62,6 @@
"render-tests/regressions/mapbox-gl-js#2467": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
"render-tests/regressions/mapbox-gl-js#2762": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
"render-tests/regressions/mapbox-gl-js#2769": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
- "render-tests/regressions/mapbox-gl-js#3107": "FLAKY: do not re-enable without extensive testing - https://github.com/mapbox/mapbox-gl-native/issues/14869",
"render-tests/regressions/mapbox-gl-js#5740": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
"render-tests/regressions/mapbox-gl-js#5982": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
"render-tests/regressions/mapbox-gl-js#6655": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue",
@@ -78,21 +71,12 @@
"render-tests/regressions/mapbox-gl-js#7302": "skip - js specific",
"render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357",
"render-tests/symbol-cross-fade/chinese": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
- "render-tests/symbol-visibility/visible": "https://github.com/mapbox/mapbox-gl-native/issues/10409",
- "render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
- "render-tests/text-rotate/with-offset": "https://github.com/mapbox/mapbox-gl-native/issues/11872",
"render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601",
"render-tests/background-color/colorSpace-hcl": "needs issue",
- "render-tests/combinations/heatmap-translucent--background-opaque": "needs investigation",
"render-tests/feature-state/composite-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12613",
"render-tests/feature-state/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12613",
"render-tests/feature-state/vector-source": "https://github.com/mapbox/mapbox-gl-native/issues/12613",
- "render-tests/text-variable-anchor/pitched-rotated-debug": "https://github.com/mapbox/mapbox-gl-native/issues/14211",
- "render-tests/text-variable-anchor/rotated-offset": "https://github.com/mapbox/mapbox-gl-native/issues/14211",
"render-tests/text-variable-anchor/remember-last-placement": "skip - fails on gl-native, as symbol index is not functional at static map mode - needs issue",
- "render-tests/geojson/clustered-properties": "https://github.com/mapbox/mapbox-gl-native/issues/14043",
"render-tests/remove-feature-state/composite-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12413",
"render-tests/remove-feature-state/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12413",
"render-tests/remove-feature-state/vector-source": "https://github.com/mapbox/mapbox-gl-native/issues/12413",
@@ -110,8 +94,5 @@
"query-tests/fill-extrusion/sort-rotated": "https://github.com/mapbox/mapbox-gl-native/issues/13139",
"query-tests/fill-extrusion/sort": "https://github.com/mapbox/mapbox-gl-native/issues/13139",
"query-tests/fill-extrusion/top-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139",
- "query-tests/regressions/mapbox-gl-js#7883": "https://github.com/mapbox/mapbox-gl-native/issues/14585",
- "expression-tests/number-format/currency": "https://github.com/mapbox/mapbox-gl-native/issues/13632",
- "expression-tests/number-format/default": "https://github.com/mapbox/mapbox-gl-native/issues/13632",
- "expression-tests/number-format/precision": "https://github.com/mapbox/mapbox-gl-native/issues/13632"
+ "query-tests/regressions/mapbox-gl-js#7883": "https://github.com/mapbox/mapbox-gl-native/issues/14585"
}
diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake
deleted file mode 100644
index 9249f846e8..0000000000
--- a/platform/qt/config.cmake
+++ /dev/null
@@ -1,75 +0,0 @@
-include(platform/qt/qt.cmake)
-
-macro(mbgl_platform_core)
- target_sources(mbgl-core
- ${MBGL_QT_CORE_FILES}
- )
-
- target_include_directories(mbgl-core
- PRIVATE platform/qt
- PRIVATE platform/qt/include
- PUBLIC platform/default/include
- )
-
- target_link_libraries(mbgl-core PRIVATE
- ${MBGL_QT_CORE_LIBRARIES}
- nunicode
- )
-
- if(NOT WITH_QT_DECODERS)
- target_sources(mbgl-core
- PRIVATE platform/default/src/mbgl/util/jpeg_reader.cpp
- PRIVATE platform/default/src/mbgl/util/png_reader.cpp
- )
-
- target_add_mason_package(mbgl-core PRIVATE libjpeg-turbo)
- target_add_mason_package(mbgl-core PRIVATE libpng)
- else()
- add_definitions(-DQT_IMAGE_DECODERS)
- endif()
-
- if(NOT WITH_QT_I18N)
- target_sources(mbgl-core PRIVATE platform/default/src/mbgl/text/bidi.cpp)
- target_link_libraries(mbgl-core PRIVATE icu)
- else()
- target_sources(mbgl-core PRIVATE platform/qt/src/bidi.cpp)
- endif()
-endmacro()
-
-
-macro(mbgl_filesource)
- target_sources(mbgl-filesource
- ${MBGL_QT_FILESOURCE_FILES}
- )
-
- target_link_libraries(mbgl-filesource
- ${MBGL_QT_FILESOURCE_LIBRARIES}
- )
-endmacro()
-
-# FIXME: For now tests are disabled on Windows until we
-# get the node.js dependencies working.
-if (NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
- macro(mbgl_platform_test)
- target_sources(mbgl-test
- PRIVATE platform/qt/test/main.cpp
- PRIVATE platform/qt/test/qmapboxgl.test.cpp
- PRIVATE platform/qt/test/qmapboxgl.test.cpp
- )
-
- target_include_directories(mbgl-test
- PRIVATE platform/qt
- )
-
- set_source_files_properties(
- platform/qt/test/main.cpp
- PROPERTIES COMPILE_FLAGS -DWORK_DIRECTORY="${CMAKE_SOURCE_DIR}"
- )
-
- target_link_libraries(mbgl-test
- PRIVATE qmapboxgl
- PRIVATE mbgl-filesource
- PRIVATE -pthread
- )
- endmacro()
-endif()
diff --git a/platform/qt/qnx.cmake b/platform/qt/qnx.cmake
deleted file mode 100644
index 7033f62362..0000000000
--- a/platform/qt/qnx.cmake
+++ /dev/null
@@ -1,77 +0,0 @@
-# CMake toolchain file for QNX Environment.
-#
-# Usage:
-#
-# To use this file, you need to set the 'CMAKE_TOOLCHAIN_FILE' to point to
-# 'qnx.cmake' on the command line:
-#
-# cmake -DDCMAKE_TOOLCHAIN_FILE=platform/qt/qnx.cmake
-#
-# You will also need to provide the locations of the QNX HOST and TARGET locations.
-# This can be done by setting the environment variables(QNX_HOST and QNX_TARGET) from
-# the command line or running the environment setup script provided by the QNX Software
-# Development Platform:
-#
-# source <SDP_DIRECTORY>/qnxsdp-env.sh
-#
-# Options:
-#
-# QCC_COMPILER_TARGET:
-# The compiler name. Default: gcc_ntox86_64.
-# QCC_NTOARCH:
-# The architecture to compile for. Default: x86_64.
-# QNX_HOST:
-# Environment variable to QNX host build tools location.
-# QNX_TARGET:
-# Environment variable to QNX target location.
-
-set(CMAKE_SYSTEM_NAME QNX)
-set(CMAKE_HOST_SYSTEM_NAME QNX)
-SET(CMAKE_SYSTEM_VERSION 7.0.0)
-set(QCC_COMPILER_TARGET "$ENV{QCC_COMPILER_TARGET}")
-set(QCC_NTOARCH "$ENV{QCC_NTOARCH}")
-
-#Check environment variables
-if ("$ENV{QNX_HOST}" STREQUAL "")
- message(FATAL_ERROR "QNX_HOST environment variable not set")
-endif()
-
-if ("$ENV{QNX_TARGET}" STREQUAL "")
- message(FATAL_ERROR "QNX_TARGET environment variable not set")
-endif()
-
-set(QNX_HOST "$ENV{QNX_HOST}")
-set(QNX_TARGET "$ENV{QNX_TARGET}")
-
-# Use 'qcc' instead of nto${QCC_NTOARCH}-gcc and nto${QCC_NTOARCH}-gcc++ once
-# the issue https://gitlab.kitware.com/cmake/cmake/issues/17126 is resolved.
-#set(CMAKE_C_COMPILER qcc)
-#set(CMAKE_C_COMPILER_TARGET ${QCC_COMPILER_TARGET})
-#set(CMAKE_CXX_COMPILER QCC)
-#set(CMAKE_CXX_COMPILER_TARGET ${QCC_COMPILER_TARGET})
-
-set(CMAKE_C_COMPILER ${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-gcc)
-set(CMAKE_CXX_COMPILER "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-g++")
-
-set(CMAKE_LINKER "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-ld" CACHE PATH "QNX linker program" FORCE)
-set(CMAKE_AR "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-ar" CACHE PATH "QNX ar program" FORCE)
-set(CMAKE_NM "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-nm" CACHE PATH "QNX nm program" FORCE)
-set(CMAKE_OBJCOPY "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-objcopy" CACHE PATH "QNX objcopy program" FORCE)
-set(CMAKE_OBJDUMP "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-objdump" CACHE PATH "QNX objdump program" FORCE)
-set(CMAKE_RANLIB "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-ranlib" CACHE PATH "QNX ranlib program" FORCE)
-set(CMAKE_STRIP "${QNX_HOST}/usr/bin/nto${QCC_NTOARCH}-strip" CACHE PATH "QNX strip program" FORCE)
-set (CMAKE_SH "${QNX_HOST}/usr/bin/sh" CACHE PATH "QNX shell program" FORCE)
-
-set(CMAKE_C_FLAGS_DEBUG "-g")
-set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG")
-set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
-set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g")
-set(CMAKE_CXX_FLAGS_DEBUG "-g -D_DEBUG")
-set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
-set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
-set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
-
-set(CMAKE_FIND_ROOT_PATH "${QNX_TARGET}")
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake
deleted file mode 100644
index 33acb7a030..0000000000
--- a/platform/qt/qt.cmake
+++ /dev/null
@@ -1,187 +0,0 @@
-# This file is to be reused by target platforms that don't
-# support `mason` (i.e. Yocto). Do not add any `mason` macro.
-
-option(WITH_QT_DECODERS "Use builtin Qt image decoders" OFF)
-option(WITH_QT_I18N "Use builtin Qt i18n support" OFF)
-
-add_definitions("-D__QT__")
-
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTORCC ON)
-
-set(MBGL_QT_CORE_FILES
- # Headless view
- PRIVATE platform/default/src/mbgl/gfx/headless_frontend.cpp
- PRIVATE platform/default/include/mbgl/gfx/headless_frontend.hpp
- PRIVATE platform/default/src/mbgl/gfx/headless_backend.cpp
- PRIVATE platform/default/include/mbgl/gfx/headless_backend.hpp
- PRIVATE platform/default/src/mbgl/gl/headless_backend.cpp
- PRIVATE platform/default/include/mbgl/gl/headless_backend.hpp
- PRIVATE platform/qt/src/headless_backend_qt.cpp
-
- # Thread
- PRIVATE platform/qt/src/thread_local.cpp
-
- # Platform integration
- PRIVATE platform/qt/src/async_task.cpp
- PRIVATE platform/qt/src/async_task_impl.hpp
- PRIVATE platform/qt/src/local_glyph_rasterizer.cpp
- PRIVATE platform/qt/src/qt_logging.cpp
- PRIVATE platform/qt/src/qt_image.cpp
- PRIVATE platform/qt/src/run_loop.cpp
- PRIVATE platform/qt/src/run_loop_impl.hpp
- PRIVATE platform/qt/src/string_stdlib.cpp
- PRIVATE platform/qt/src/timer.cpp
- PRIVATE platform/qt/src/timer_impl.hpp
- PRIVATE platform/qt/src/utf.cpp
- PRIVATE platform/qt/src/gl_functions.cpp
- PRIVATE platform/qt/src/format_number.cpp
-
- PRIVATE platform/default/src/mbgl/text/collator.cpp
- PRIVATE platform/default/src/mbgl/text/unaccent.cpp
- PRIVATE platform/default/include/mbgl/text/unaccent.hpp
-
- #Layer manager
- PRIVATE platform/default/src/mbgl/layermanager/layer_manager.cpp
-)
-
-set(MBGL_QT_FILESOURCE_FILES
- # File source
- PRIVATE platform/default/src/mbgl/storage/file_source.cpp
- PRIVATE platform/qt/src/http_file_source.cpp
- PRIVATE platform/qt/src/http_file_source.hpp
- PRIVATE platform/qt/src/http_request.cpp
- PRIVATE platform/qt/src/http_request.hpp
-
- # Database
- PRIVATE platform/qt/src/sqlite3.cpp
-)
-
-# Shared library
-add_library(qmapboxgl SHARED
- platform/qt/include/qmapbox.hpp
- platform/qt/include/qmapboxgl.hpp
- platform/qt/src/qt_conversion.hpp
- platform/qt/src/qt_geojson.cpp
- platform/qt/src/qt_geojson.hpp
- platform/qt/src/qmapbox.cpp
- platform/qt/src/qmapboxgl.cpp
- platform/qt/src/qmapboxgl_p.hpp
- platform/qt/src/qmapboxgl_map_observer.cpp
- platform/qt/src/qmapboxgl_map_observer.hpp
- platform/qt/src/qmapboxgl_map_renderer.cpp
- platform/qt/src/qmapboxgl_map_renderer.hpp
- platform/qt/src/qmapboxgl_renderer_backend.cpp
- platform/qt/src/qmapboxgl_renderer_backend.hpp
- platform/qt/src/qmapboxgl_scheduler.cpp
- platform/qt/src/qmapboxgl_scheduler.hpp
- platform/default/include/mbgl/util/default_styles.hpp
-)
-
-target_include_directories(qmapboxgl
- PUBLIC platform/qt/include
- PRIVATE src
-)
-
-target_compile_definitions(qmapboxgl
- PRIVATE "-DQT_BUILD_MAPBOXGL_LIB"
-)
-
-target_link_libraries(qmapboxgl
- PRIVATE mbgl-core
- PRIVATE mbgl-filesource
- ${MBGL_QT_CORE_LIBRARIES}
- ${MBGL_QT_FILESOURCE_LIBRARIES}
-)
-
-# C++ app
-add_executable(mbgl-qt
- platform/qt/app/main.cpp
- platform/qt/app/mapwindow.cpp
- platform/qt/app/mapwindow.hpp
- platform/qt/resources/common.qrc
-)
-
-target_compile_options(qmapboxgl
- PRIVATE -std=c++03
-)
-
-target_link_libraries(mbgl-qt
- PRIVATE qmapboxgl
-)
-
-find_package(Qt5Core REQUIRED)
-find_package(Qt5Gui REQUIRED)
-find_package(Qt5Network REQUIRED)
-find_package(Qt5OpenGL REQUIRED)
-find_package(Qt5Widgets REQUIRED)
-find_package(Qt5Sql REQUIRED)
-
-# Qt5 always build OpenGL ES2 which is the compatibility
-# mode. Qt5 will take care of translating the desktop
-# version of OpenGL to ES2.
-add_definitions("-DMBGL_USE_GLES2")
-
-set(MBGL_QT_CORE_LIBRARIES
- PUBLIC Qt5::Core
- PUBLIC Qt5::Gui
- PUBLIC Qt5::OpenGL
-)
-
-set(MBGL_QT_FILESOURCE_LIBRARIES
- PUBLIC Qt5::Network
- PUBLIC Qt5::Sql
-)
-
-target_link_libraries(mbgl-qt
- PRIVATE Qt5::Widgets
-)
-
-xcode_create_scheme(TARGET mbgl-qt)
-
-# OS specific configurations
-if (MASON_PLATFORM STREQUAL "osx" OR MASON_PLATFORM STREQUAL "ios")
- list(APPEND MBGL_QT_CORE_FILES
- PRIVATE platform/darwin/src/nsthread.mm
- )
- list(APPEND MBGL_QT_CORE_LIBRARIES
- PRIVATE "-framework Foundation"
- )
-elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
- list(APPEND MBGL_QT_CORE_FILES
- PRIVATE platform/default/src/mbgl/util/thread.cpp
- )
-elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
- add_definitions("-DQT_COMPILING_QIMAGE_COMPAT_CPP")
- add_definitions("-DRAPIDJSON_HAS_CXX11_RVALUE_REFS")
- add_definitions("-D_USE_MATH_DEFINES")
-
- add_definitions("-Wno-deprecated-declarations")
- add_definitions("-Wno-macro-redefined")
- add_definitions("-Wno-microsoft-exception-spec")
- add_definitions("-Wno-unknown-argument")
- add_definitions("-Wno-unknown-warning-option")
- add_definitions("-Wno-unused-command-line-argument")
- add_definitions("-Wno-unused-local-typedef")
- add_definitions("-Wno-unused-private-field")
- add_definitions("-Wno-inconsistent-missing-override")
-
- list(APPEND MBGL_QT_CORE_FILES
- PRIVATE platform/qt/src/thread.cpp
- )
-elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "QNX")
- list(APPEND MBGL_QT_CORE_FILES
- PRIVATE platform/qt/src/thread.cpp
- )
- add_definitions("-Wno-narrowing")
-endif()
-
-add_custom_command(
- TARGET qmapboxgl
- POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy_directory
- ${CMAKE_SOURCE_DIR}/platform/qt/include
- ${CMAKE_CURRENT_BINARY_DIR}/platform/qt/include
-)
-
-xcode_create_scheme(TARGET qmapboxgl)
diff --git a/platform/qt/src/local_glyph_rasterizer.cpp b/platform/qt/src/local_glyph_rasterizer.cpp
index 3f8df5d876..f62975b377 100644
--- a/platform/qt/src/local_glyph_rasterizer.cpp
+++ b/platform/qt/src/local_glyph_rasterizer.cpp
@@ -1,5 +1,6 @@
#include <mbgl/text/local_glyph_rasterizer.hpp>
#include <mbgl/util/i18n.hpp>
+#include <mbgl/util/constants.hpp>
#include <QtCore/QFile>
#include <QtGui/QFont>
@@ -17,13 +18,15 @@ public:
optional<std::string> fontFamily;
QFont font;
+ optional<QFontMetrics> metrics;
};
LocalGlyphRasterizer::Impl::Impl(const optional<std::string> fontFamily_)
: fontFamily(fontFamily_) {
if (isConfigured()) {
font.setFamily(QString::fromStdString(*fontFamily));
- font.setPixelSize(24);
+ font.setPixelSize(util::ONE_EM);
+ metrics = QFontMetrics(font);
}
}
@@ -39,29 +42,33 @@ LocalGlyphRasterizer::~LocalGlyphRasterizer() {
}
bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID) {
- return util::i18n::allowsFixedWidthGlyphGeneration(glyphID) && impl->isConfigured();
+ return impl->isConfigured() && impl->metrics->inFont(glyphID) && util::i18n::allowsFixedWidthGlyphGeneration(glyphID);
}
Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack&, GlyphID glyphID) {
Glyph glyph;
glyph.id = glyphID;
- QFontMetrics metrics(impl->font);
- Size size(metrics.width(glyphID), metrics.height());
+ if (!impl->isConfigured()) {
+ assert(false);
+ return glyph;
+ }
- glyph.metrics.width = size.width;
- glyph.metrics.height = size.height;
+ glyph.metrics.width = impl->metrics->width(glyphID);
+ glyph.metrics.height = impl->metrics->height();
glyph.metrics.left = 3;
glyph.metrics.top = -8;
- glyph.metrics.advance = metrics.width(glyphID);
+ glyph.metrics.advance = glyph.metrics.width;
+ // Set width of a glyph's backing image to be util::ONE_EM.
+ Size size(util::ONE_EM, glyph.metrics.height);
QImage image(QSize(size.width, size.height), QImage::Format_Alpha8);
image.fill(qRgba(0, 0, 0, 0));
-
QPainter painter(&image);
painter.setFont(impl->font);
painter.setRenderHints(QPainter::TextAntialiasing);
- painter.drawText(QPointF(0, metrics.ascent()), QString(QChar(glyphID)));
+ // Render at constant baseline, to align with glyphs that are rendered by node-fontnik.
+ painter.drawText(QPointF(0, 20), QString(QChar(glyphID)));
auto img = std::make_unique<uint8_t[]>(image.byteCount());
memcpy(img.get(), image.constBits(), image.byteCount());
diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp
index 4a842026cd..00af666b0f 100644
--- a/platform/qt/src/qmapboxgl_map_observer.cpp
+++ b/platform/qt/src/qmapboxgl_map_observer.cpp
@@ -77,9 +77,9 @@ void QMapboxGLMapObserver::onWillStartRenderingFrame()
emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame);
}
-void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode)
+void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus status)
{
- if (mode == mbgl::MapObserver::RenderMode::Partial) {
+ if (status.mode == mbgl::MapObserver::RenderMode::Partial) {
emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame);
} else {
emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered);
diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp
index a12e5e9c70..d9e51db28f 100644
--- a/platform/qt/src/qmapboxgl_map_observer.hpp
+++ b/platform/qt/src/qmapboxgl_map_observer.hpp
@@ -28,7 +28,7 @@ public:
void onDidFinishLoadingMap() final;
void onDidFailLoadingMap(mbgl::MapLoadError, const std::string&) final;
void onWillStartRenderingFrame() final;
- void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final;
+ void onDidFinishRenderingFrame(mbgl::MapObserver::RenderFrameStatus) final;
void onWillStartRenderingMap() final;
void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final;
void onDidFinishLoadingStyle() final;
diff --git a/platform/qt/src/qmapboxgl_map_renderer.cpp b/platform/qt/src/qmapboxgl_map_renderer.cpp
index eba50af6bf..8423470323 100644
--- a/platform/qt/src/qmapboxgl_map_renderer.cpp
+++ b/platform/qt/src/qmapboxgl_map_renderer.cpp
@@ -28,7 +28,7 @@ static auto *getScheduler() {
QMapboxGLMapRenderer::QMapboxGLMapRenderer(qreal pixelRatio, QMapboxGLSettings::GLContextMode mode, const QString &localFontFamily)
: m_backend(static_cast<mbgl::gfx::ContextMode>(mode)),
- m_renderer(std::make_unique<mbgl::Renderer>(m_backend, pixelRatio, mbgl::optional<std::string> {},
+ m_renderer(std::make_unique<mbgl::Renderer>(m_backend, pixelRatio,
localFontFamily.isEmpty() ? mbgl::nullopt : mbgl::optional<std::string> { localFontFamily.toStdString() }))
, m_forceScheduler(needsToForceScheduler())
{
diff --git a/platform/qt/src/qmapboxgl_renderer_observer.hpp b/platform/qt/src/qmapboxgl_renderer_observer.hpp
index ee340113ff..78dd6b91d6 100644
--- a/platform/qt/src/qmapboxgl_renderer_observer.hpp
+++ b/platform/qt/src/qmapboxgl_renderer_observer.hpp
@@ -37,8 +37,8 @@ public:
delegate.invoke(&mbgl::RendererObserver::onWillStartRenderingFrame);
}
- void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) final {
- delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded);
+ void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded, bool placementChanged) final {
+ delegate.invoke(&mbgl::RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded, placementChanged);
}
void onDidFinishRenderingMap() final {
diff --git a/platform/qt/test/qmapboxgl.test.cpp b/platform/qt/test/qmapboxgl.test.cpp
index de0314f0ee..604bdde896 100644
--- a/platform/qt/test/qmapboxgl.test.cpp
+++ b/platform/qt/test/qmapboxgl.test.cpp
@@ -1,11 +1,14 @@
#include "qmapboxgl.test.hpp"
-#include <mbgl/util/io.hpp>
-
#include <QMapbox>
+#include <QFile>
+#include <QGuiApplication>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
+#include <QTextStream>
+
+#include <mbgl/test/util.hpp>
QMapboxGLTest::QMapboxGLTest() : size(512, 512), fbo((assert(widget.context()->isValid()), widget.makeCurrent(), size)), map(nullptr, settings, size) {
connect(&map, SIGNAL(mapChanged(QMapboxGL::MapChange)),
@@ -43,8 +46,12 @@ void QMapboxGLTest::onNeedsRendering() {
TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleJson)) {
- QString json = QString::fromStdString(
- mbgl::util::read_file("test/fixtures/resources/style_vector.json"));
+ QFile f("test/fixtures/resources/style_vector.json");
+
+ ASSERT_TRUE(f.open(QFile::ReadOnly | QFile::Text));
+
+ QTextStream in(&f);
+ QString json = in.readAll();
map.setStyleJson(json);
ASSERT_EQ(map.styleJson(), json);
diff --git a/platform/qt/test/qmapboxgl.test.hpp b/platform/qt/test/qmapboxgl.test.hpp
index 43a7c123b7..3591dbc9d8 100644
--- a/platform/qt/test/qmapboxgl.test.hpp
+++ b/platform/qt/test/qmapboxgl.test.hpp
@@ -1,11 +1,10 @@
-#include <mbgl/test/util.hpp>
-
-#include <QApplication>
#include <QMapboxGL>
#include <QGLWidget>
#include <QGLFramebufferObject>
+#include <gtest/gtest.h>
+
class QMapboxGLTest : public QObject, public ::testing::Test {
Q_OBJECT
diff --git a/render-test/allocation_index.cpp b/render-test/allocation_index.cpp
new file mode 100644
index 0000000000..144c18ddd5
--- /dev/null
+++ b/render-test/allocation_index.cpp
@@ -0,0 +1,101 @@
+#include "allocation_index.hpp"
+
+#include <atomic>
+#include <cassert>
+#include <cstdlib>
+#include <mutex>
+#include <unordered_map>
+
+namespace {
+
+std::atomic_size_t indexedMemorySize{0};
+std::atomic_size_t indexedMemoryPeak{0};
+std::atomic_size_t allocationsCount{0};
+std::unordered_map<void*, size_t> memoryIndex;
+std::atomic_bool suppresIndexing{false};
+std::atomic_bool active{false};
+std::mutex indexMutex;
+
+class FlagGuard {
+public:
+ explicit FlagGuard(std::atomic_bool& flag_)
+ : flag(flag_) { flag = true; }
+ ~FlagGuard() { flag = false; }
+
+private:
+ std::atomic_bool& flag;
+};
+
+void addToIndex(std::size_t sz, void* ptr) {
+ std::lock_guard<std::mutex> mlk(indexMutex);
+ FlagGuard flk(suppresIndexing);
+ allocationsCount++;
+ indexedMemorySize += sz;
+ if (indexedMemoryPeak < indexedMemorySize) indexedMemoryPeak = size_t(indexedMemorySize);
+ memoryIndex[ptr] = sz;
+}
+
+void removeFromIndex(void* ptr) {
+ std::lock_guard<std::mutex> mlk(indexMutex);
+ FlagGuard flk(suppresIndexing);
+ auto it = memoryIndex.find(ptr);
+ if (it == memoryIndex.end()) return;
+
+ assert(indexedMemorySize >= it->second);
+ indexedMemorySize -= it->second;
+ memoryIndex.erase(it);
+}
+
+inline bool canModifyIndex() {
+ return active && !suppresIndexing;
+}
+
+} // namespace
+
+// static
+void AllocationIndex::setActive(bool active_) {
+ active = active_;
+}
+
+// static
+bool AllocationIndex::isActive() {
+ return active;
+}
+
+// static
+void AllocationIndex::reset() {
+ std::lock_guard<std::mutex> mlk(indexMutex);
+ FlagGuard flk(suppresIndexing);
+ memoryIndex.clear();
+ indexedMemorySize = 0;
+ allocationsCount = 0;
+ indexedMemoryPeak = 0;
+}
+
+// static
+void* AllocationIndex::allocate(size_t size) {
+ void *ptr = std::malloc(size);
+ if (ptr && canModifyIndex()) addToIndex(size, ptr);
+ return ptr;
+}
+
+// static
+void AllocationIndex::deallocate(void* ptr) noexcept {
+ if (canModifyIndex()) removeFromIndex(ptr);
+ std::free(ptr);
+}
+
+// static
+size_t AllocationIndex::getAllocatedSize() {
+ return indexedMemorySize;
+}
+
+// static
+size_t AllocationIndex::getAllocationsCount() {
+ return allocationsCount;
+}
+
+// static
+size_t AllocationIndex::getAllocatedSizePeak() {
+ return indexedMemoryPeak;
+}
diff --git a/render-test/allocation_index.hpp b/render-test/allocation_index.hpp
new file mode 100644
index 0000000000..71da441c1f
--- /dev/null
+++ b/render-test/allocation_index.hpp
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <cstddef>
+
+class AllocationIndex {
+public:
+ AllocationIndex() = delete;
+ /**
+ * @brief Starts/stops allocated memory indexing.
+ */
+ static void setActive(bool);
+ /**
+ * @brief Returns memory indexing activity status.
+ */
+ static bool isActive();
+ /**
+ * @brief Resets the allocated memory index and all statistics data.
+ */
+ static void reset();
+ /**
+ * @brief Same as `malloc()` + indexes the allocated data,
+ * if indexing is active.
+ *
+ * @return void*
+ */
+ static void* allocate(size_t);
+ /**
+ * @brief Same as `free()` + removes the corresponding data from index,
+ * if these data are present in the index and indexing is active.
+ *
+ * @return void*
+ */
+ static void deallocate(void*) noexcept;
+ /**
+ * @brief Returns the size (in bytes) of allocated data, currently present in the index.
+ *
+ * @return size_t
+ */
+ static size_t getAllocatedSize();
+ /**
+ * @brief Returns the total amount of allocations since indexing start.
+ *
+ * @return size_t
+ */
+ static size_t getAllocationsCount();
+
+ /**
+ * @brief Returns the maximum size (in bytes) of the allocated data in the index, since last `reset()` call.
+ *
+ * @return size_t
+ */
+ static size_t getAllocatedSizePeak();
+};
diff --git a/render-test/expected/debug/collision-lines-overscaled/expected.png b/render-test/expected/debug/collision-lines-overscaled/expected.png
new file mode 100644
index 0000000000..38eb0d2da6
--- /dev/null
+++ b/render-test/expected/debug/collision-lines-overscaled/expected.png
Binary files differ
diff --git a/render-test/expected/debug/collision-lines-pitched/expected.png b/render-test/expected/debug/collision-lines-pitched/expected.png
new file mode 100644
index 0000000000..416d7d5715
--- /dev/null
+++ b/render-test/expected/debug/collision-lines-pitched/expected.png
Binary files differ
diff --git a/render-test/expected/debug/collision-lines/expected.png b/render-test/expected/debug/collision-lines/expected.png
new file mode 100644
index 0000000000..3f4790a585
--- /dev/null
+++ b/render-test/expected/debug/collision-lines/expected.png
Binary files differ
diff --git a/render-test/expected/debug/collision-pitched-wrapped/expected.png b/render-test/expected/debug/collision-pitched-wrapped/expected.png
new file mode 100644
index 0000000000..9b718c09c0
--- /dev/null
+++ b/render-test/expected/debug/collision-pitched-wrapped/expected.png
Binary files differ
diff --git a/render-test/expected/symbol-visibility/visible/expected.png b/render-test/expected/symbol-visibility/visible/expected.png
new file mode 100644
index 0000000000..8da157772a
--- /dev/null
+++ b/render-test/expected/symbol-visibility/visible/expected.png
Binary files differ
diff --git a/render-test/expected/text-pitch-alignment/auto-text-rotation-alignment-map/expected.png b/render-test/expected/text-pitch-alignment/auto-text-rotation-alignment-map/expected.png
new file mode 100644
index 0000000000..cd690ca152
--- /dev/null
+++ b/render-test/expected/text-pitch-alignment/auto-text-rotation-alignment-map/expected.png
Binary files differ
diff --git a/render-test/expected/text-pitch-alignment/map-text-rotation-alignment-map/expected.png b/render-test/expected/text-pitch-alignment/map-text-rotation-alignment-map/expected.png
new file mode 100644
index 0000000000..cd690ca152
--- /dev/null
+++ b/render-test/expected/text-pitch-alignment/map-text-rotation-alignment-map/expected.png
Binary files differ
diff --git a/render-test/expected/text-pitch-alignment/viewport-text-rotation-alignment-map/expected.png b/render-test/expected/text-pitch-alignment/viewport-text-rotation-alignment-map/expected.png
new file mode 100644
index 0000000000..764d4a0b24
--- /dev/null
+++ b/render-test/expected/text-pitch-alignment/viewport-text-rotation-alignment-map/expected.png
Binary files differ
diff --git a/render-test/expected/text-variable-anchor/pitched-rotated-debug/expected.png b/render-test/expected/text-variable-anchor/pitched-rotated-debug/expected.png
new file mode 100644
index 0000000000..4e3d012844
--- /dev/null
+++ b/render-test/expected/text-variable-anchor/pitched-rotated-debug/expected.png
Binary files differ
diff --git a/render-test/expected/text-variable-anchor/rotated-offset/expected.png b/render-test/expected/text-variable-anchor/rotated-offset/expected.png
new file mode 100644
index 0000000000..13690d147c
--- /dev/null
+++ b/render-test/expected/text-variable-anchor/rotated-offset/expected.png
Binary files differ
diff --git a/render-test/linux-ignores.json b/render-test/linux-ignores.json
new file mode 100644
index 0000000000..d5a1a85c6c
--- /dev/null
+++ b/render-test/linux-ignores.json
@@ -0,0 +1,11 @@
+{
+ "render-tests/regressions/mapbox-gl-js#7066": "Failing with mbgl-render-test",
+ "render-tests/regressions/mapbox-gl-js#5642": "Failing with mbgl-render-test",
+ "render-tests/line-pattern/opacity": "Flaky on Linux: https://github.com/mapbox/mapbox-gl-native/issues/15320",
+ "render-tests/fill-opacity/zoom-and-property-function-pattern": "Flaky on Linux: https://github.com/mapbox/mapbox-gl-native/issues/15322",
+ "render-tests/fill-pattern/literal": "Flaky on Linux: https://github.com/mapbox/mapbox-gl-native/issues/14423",
+ "render-tests/fill-pattern/opacity": "Flaky on Linux: https://github.com/mapbox/mapbox-gl-native/issues/14870",
+ "render-tests/fill-pattern/zoomed": "Flaky on Linux: https://github.com/mapbox/mapbox-gl-native/issues/14768",
+ "render-tests/line-translate/literal": "Flaky on Linux: https://github.com/mapbox/mapbox-gl-native/issues/14859"
+}
+
diff --git a/render-test/mac-ignores.json b/render-test/mac-ignores.json
new file mode 100644
index 0000000000..87bef31c5c
--- /dev/null
+++ b/render-test/mac-ignores.json
@@ -0,0 +1,5 @@
+{
+ "render-tests/regressions/mapbox-gl-js#7066": "Failing with mbgl-render-test",
+ "render-tests/regressions/mapbox-gl-js#5642": "Failing with mbgl-render-test"
+}
+
diff --git a/render-test/main.cpp b/render-test/main.cpp
index 35f0cdea30..fcdbe3ab55 100644
--- a/render-test/main.cpp
+++ b/render-test/main.cpp
@@ -1,7 +1,8 @@
+#include "allocation_index.hpp"
+
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/io.hpp>
-#include "filesystem.hpp"
#include "metadata.hpp"
#include "parser.hpp"
#include "runner.hpp"
@@ -18,28 +19,35 @@
#define ANSI_COLOR_LIGHT_GRAY "\x1b[90m"
#define ANSI_COLOR_RESET "\x1b[0m"
+#if !defined(SANITIZE)
+void* operator new(std::size_t sz) {
+ void* ptr = AllocationIndex::allocate(sz);
+ if (!ptr) throw std::bad_alloc{};
+
+ return ptr;
+}
+
+void operator delete(void* ptr) noexcept {
+ AllocationIndex::deallocate(ptr);
+}
+
+void operator delete(void* ptr, size_t) noexcept {
+ AllocationIndex::deallocate(ptr);
+}
+#endif
+
int main(int argc, char** argv) {
bool recycleMap;
bool shuffle;
uint32_t seed;
std::string testRootPath;
- std::vector<std::string> ids;
+ std::vector<TestPaths> testPaths;
- std::tie(recycleMap, shuffle, seed, testRootPath, ids) = parseArguments(argc, argv);
+ std::tie(recycleMap, shuffle, seed, testRootPath, testPaths) = parseArguments(argc, argv);
const std::string::size_type rootLength = testRootPath.length();
const auto ignores = parseIgnores();
- // Recursively traverse through the test paths and collect test directories containing "style.json".
- std::vector<mbgl::filesystem::path> testPaths;
- for (const auto& id : ids) {
- for (auto& testPath : mbgl::filesystem::recursive_directory_iterator(mbgl::filesystem::path(id))) {
- if (testPath.path().filename() == "style.json") {
- testPaths.push_back(testPath);
- }
- }
- }
-
if (shuffle) {
printf(ANSI_COLOR_YELLOW "Shuffle seed: %d" ANSI_COLOR_RESET "\n", seed);
@@ -67,7 +75,7 @@ int main(int argc, char** argv) {
std::string& status = metadata.status;
std::string& color = metadata.color;
- id = testPath.remove_filename().string();
+ id = testPath.defaultExpectations();
id = id.substr(rootLength + 1, id.length() - rootLength - 2);
bool shouldIgnore = false;
@@ -86,7 +94,7 @@ int main(int argc, char** argv) {
bool errored = !metadata.errorMessage.empty();
if (!errored) {
- errored = runner.run(metadata) && !metadata.errorMessage.empty();
+ errored = !runner.run(metadata) || !metadata.errorMessage.empty();
}
bool passed = !errored && !metadata.diff.empty() && metadata.difference <= metadata.allowed;
diff --git a/render-test/metadata.hpp b/render-test/metadata.hpp
index 4be83a5436..d23a0fb296 100644
--- a/render-test/metadata.hpp
+++ b/render-test/metadata.hpp
@@ -7,6 +7,8 @@
#include "filesystem.hpp"
+#include <map>
+
struct TestStatistics {
TestStatistics() = default;
@@ -17,10 +19,36 @@ struct TestStatistics {
uint32_t passedTests = 0;
};
+struct TestPaths {
+ mbgl::filesystem::path stylePath;
+ std::vector<mbgl::filesystem::path> expectations;
+
+ std::string defaultExpectations() const {
+ assert(!expectations.empty());
+ return expectations.front().string();
+ }
+};
+
+struct MemoryProbe {
+ MemoryProbe() = default;
+ MemoryProbe(size_t peak_, size_t allocations_)
+ : peak(peak_)
+ , allocations(allocations_) {}
+
+ size_t peak;
+ size_t allocations;
+};
+
+class TestMetrics {
+public:
+ bool isEmpty() const { return memory.empty(); }
+ std::map<std::string, MemoryProbe> memory;
+};
+
struct TestMetadata {
TestMetadata() = default;
- mbgl::filesystem::path path;
+ TestPaths paths;
mbgl::JSDocument document;
mbgl::Size size{ 512u, 512u };
@@ -49,4 +77,7 @@ struct TestMetadata {
std::string errorMessage;
double difference = 0.0;
-}; \ No newline at end of file
+
+ TestMetrics metrics;
+ TestMetrics expectedMetrics;
+};
diff --git a/render-test/parser.cpp b/render-test/parser.cpp
index 089a5c45c9..9b462dee72 100644
--- a/render-test/parser.cpp
+++ b/render-test/parser.cpp
@@ -13,8 +13,10 @@
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>
-#include "parser.hpp"
+#include "filesystem.hpp"
#include "metadata.hpp"
+#include "parser.hpp"
+#include "runner.hpp"
#include <sstream>
#include <regex>
@@ -161,6 +163,22 @@ mbgl::optional<std::string> localizeMapboxTilesetURL(const std::string& url) {
return getIntegrationPath(url, "tilesets/", regex);
}
+TestPaths makeTestPaths(mbgl::filesystem::path stylePath) {
+ std::vector<mbgl::filesystem::path> expectations{ stylePath };
+ expectations.front().remove_filename();
+
+ const static std::regex regex{ TestRunner::getBasePath() };
+ for (const std::string& path : TestRunner::getPlatformExpectationsPaths()) {
+ expectations.emplace_back(std::regex_replace(expectations.front().string(), regex, path));
+ assert(!expectations.back().empty());
+ }
+
+ return {
+ std::move(stylePath),
+ std::move(expectations)
+ };
+}
+
} // namespace
JSONReply readJson(const mbgl::filesystem::path& jsonPath) {
@@ -186,6 +204,29 @@ std::string serializeJsonValue(const mbgl::JSValue& value) {
return buffer.GetString();
}
+std::string serializeMetrics(const TestMetrics& metrics) {
+ rapidjson::StringBuffer s;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(s);
+
+ writer.StartObject();
+ // Start memory section.
+ writer.Key("memory");
+ writer.StartArray();
+ for (const auto& memoryProbe : metrics.memory) {
+ assert(!memoryProbe.first.empty());
+ writer.StartArray();
+ writer.String(memoryProbe.first.c_str());
+ writer.Uint64(memoryProbe.second.peak);
+ writer.Uint64(memoryProbe.second.allocations);
+ writer.EndArray();
+ }
+ // End memory section.
+ writer.EndArray();
+ writer.EndObject();
+
+ return s.GetString();
+}
+
std::vector<std::string> readExpectedEntries(const mbgl::filesystem::path& base) {
static const std::regex regex(".*expected.*.png");
@@ -215,6 +256,8 @@ ArgumentsTuple parseArguments(int argc, char** argv) {
{ "seed" });
args::ValueFlag<std::string> testPathValue(argumentParser, "rootPath", "Test root rootPath",
{ 'p', "rootPath" });
+ args::ValueFlag<std::regex> testFilterValue(argumentParser, "filter", "Test filter regex",
+ { 'f', "filter" });
args::PositionalList<std::string> testNameValues(argumentParser, "URL", "Test name(s)");
try {
@@ -236,34 +279,72 @@ ArgumentsTuple parseArguments(int argc, char** argv) {
mbgl::Log::Info(mbgl::Event::General, stream.str());
mbgl::Log::Error(mbgl::Event::General, e.what());
exit(2);
+ } catch (const std::regex_error& e) {
+ mbgl::Log::Error(mbgl::Event::General, "Invalid filter regular expression: %s", e.what());
+ exit(3);
}
- const std::string testDefaultPath =
- std::string(TEST_RUNNER_ROOT_PATH).append("/mapbox-gl-js/test/integration/render-tests");
+ mbgl::filesystem::path rootPath {testPathValue ? args::get(testPathValue) : TestRunner::getBasePath()};
+ if (!mbgl::filesystem::exists(rootPath)) {
+ mbgl::Log::Error(mbgl::Event::General, "Provided rootPath '%s' does not exist.", rootPath.string().c_str());
+ exit(4);
+ }
- std::vector<std::string> ids;
+ std::vector<mbgl::filesystem::path> paths;
for (const auto& id : args::get(testNameValues)) {
- ids.emplace_back(testDefaultPath + "/" + id);
+ paths.emplace_back(TestRunner::getBasePath() + "/" + id);
}
- if (ids.empty()) {
- ids.emplace_back(testDefaultPath);
+ if (paths.empty()) {
+ paths.emplace_back(TestRunner::getBasePath());
+ }
+
+ // Recursively traverse through the test paths and collect test directories containing "style.json".
+ std::vector<TestPaths> testPaths;
+ testPaths.reserve(paths.size());
+ for (const auto& path : paths) {
+ if (!mbgl::filesystem::exists(path)) {
+ mbgl::Log::Warning(mbgl::Event::General, "Provided test folder '%s' does not exist.", path.string().c_str());
+ continue;
+ }
+ for (auto& testPath : mbgl::filesystem::recursive_directory_iterator(path)) {
+ // Skip paths that fail regexp match.
+ if (testFilterValue && !std::regex_match(testPath.path().string(), args::get(testFilterValue))) {
+ continue;
+ }
+ if (testPath.path().filename() == "style.json") {
+ testPaths.emplace_back(makeTestPaths(testPath));
+ }
+ }
}
return ArgumentsTuple {
recycleMapFlag ? args::get(recycleMapFlag) : false,
shuffleFlag ? args::get(shuffleFlag) : false, seedValue ? args::get(seedValue) : 1u,
- testPathValue ? args::get(testPathValue) : testDefaultPath, ids
+ testPathValue ? args::get(testPathValue) : TestRunner::getBasePath(),
+ std::move(testPaths)
};
}
std::vector<std::pair<std::string, std::string>> parseIgnores() {
std::vector<std::pair<std::string, std::string>> ignores;
- auto path = mbgl::filesystem::path(TEST_RUNNER_ROOT_PATH).append("platform/node/test/ignores.json");
+ auto mainIgnoresPath = mbgl::filesystem::path(TEST_RUNNER_ROOT_PATH).append("platform/node/test/ignores.json");
- auto maybeIgnores = readJson(path.string());
- if (maybeIgnores.is<mbgl::JSDocument>()) {
+ mbgl::filesystem::path platformSpecificIgnores;
+
+#ifdef __APPLE__
+ platformSpecificIgnores = mbgl::filesystem::path(TEST_RUNNER_ROOT_PATH).append("render-test/mac-ignores.json");
+#elif __linux__
+ platformSpecificIgnores = mbgl::filesystem::path(TEST_RUNNER_ROOT_PATH).append("render-test/linux-ignores.json");
+#endif
+
+ std::vector<mbgl::filesystem::path> ignoresPaths = { mainIgnoresPath, platformSpecificIgnores };
+ for (auto path: ignoresPaths) {
+ auto maybeIgnores = readJson(path);
+ if (!maybeIgnores.is<mbgl::JSDocument>()) {
+ continue;
+ }
for (const auto& property : maybeIgnores.get<mbgl::JSDocument>().GetObject()) {
const std::string ignore = { property.name.GetString(),
property.name.GetStringLength() };
@@ -276,13 +357,43 @@ std::vector<std::pair<std::string, std::string>> parseIgnores() {
return ignores;
}
-TestMetadata parseTestMetadata(const mbgl::filesystem::path& path) {
- TestMetadata metadata;
- metadata.path = path;
+TestMetrics readExpectedMetrics(const mbgl::filesystem::path& path) {
+ TestMetrics result;
auto maybeJson = readJson(path.string());
if (!maybeJson.is<mbgl::JSDocument>()) { // NOLINT
- metadata.errorMessage = std::string("Unable to parse: ") + path.string();
+ return result;
+ }
+
+ const auto& document = maybeJson.get<mbgl::JSDocument>();
+ if (document.HasMember("memory")) {
+ const mbgl::JSValue& memoryValue = document["memory"];
+ assert(memoryValue.IsArray());
+ for (auto& probeValue : memoryValue.GetArray()) {
+ assert(probeValue.IsArray());
+ assert(probeValue.Size() >= 3u);
+ assert(probeValue[0].IsString());
+ assert(probeValue[1].IsNumber());
+ assert(probeValue[2].IsNumber());
+
+ const std::string mark { probeValue[0].GetString(), probeValue[0].GetStringLength() };
+ assert(!mark.empty());
+ result.memory.emplace(std::piecewise_construct,
+ std::forward_as_tuple(std::move(mark)),
+ std::forward_as_tuple(probeValue[1].GetUint64(), probeValue[2].GetUint64()));
+ }
+ }
+
+ return result;
+}
+
+TestMetadata parseTestMetadata(const TestPaths& paths) {
+ TestMetadata metadata;
+ metadata.paths = paths;
+
+ auto maybeJson = readJson(paths.stylePath.string());
+ if (!maybeJson.is<mbgl::JSDocument>()) { // NOLINT
+ metadata.errorMessage = std::string("Unable to parse: ") + metadata.paths.stylePath.string();
return metadata;
}
@@ -291,14 +402,14 @@ TestMetadata parseTestMetadata(const mbgl::filesystem::path& path) {
if (!metadata.document.HasMember("metadata")) {
mbgl::Log::Warning(mbgl::Event::ParseStyle, "Style has no 'metadata': %s",
- path.c_str());
+ paths.stylePath.c_str());
return metadata;
}
const mbgl::JSValue& metadataValue = metadata.document["metadata"];
if (!metadataValue.HasMember("test")) {
mbgl::Log::Warning(mbgl::Event::ParseStyle, "Style has no 'metadata.test': %s",
- path.c_str());
+ paths.stylePath.c_str());
return metadata;
}
diff --git a/render-test/parser.hpp b/render-test/parser.hpp
index be98719ab5..94fb212944 100644
--- a/render-test/parser.hpp
+++ b/render-test/parser.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "metadata.hpp"
+
#include <mbgl/util/rapidjson.hpp>
#include <mbgl/util/variant.hpp>
@@ -7,24 +9,23 @@
#include <string>
#include <vector>
-#include "filesystem.hpp"
-
-struct TestMetadata;
-struct TestStatistics;
-
using ErrorMessage = std::string;
using JSONReply = mbgl::variant<mbgl::JSDocument, ErrorMessage>;
-using ArgumentsTuple = std::tuple<bool, bool, uint32_t, std::string, std::vector<std::string>>;
+using ArgumentsTuple = std::tuple<bool, bool, uint32_t, std::string, std::vector<TestPaths>>;
JSONReply readJson(const mbgl::filesystem::path&);
std::string serializeJsonValue(const mbgl::JSValue&);
+std::string serializeMetrics(const TestMetrics&);
std::vector<std::string> readExpectedEntries(const mbgl::filesystem::path& base);
+TestMetrics readExpectedMetrics(const mbgl::filesystem::path& path);
+
ArgumentsTuple parseArguments(int argc, char** argv);
std::vector<std::pair<std::string, std::string>> parseIgnores();
-TestMetadata parseTestMetadata(const mbgl::filesystem::path& path);
+
+TestMetadata parseTestMetadata(const TestPaths& paths);
std::string createResultPage(const TestStatistics&, const std::vector<TestMetadata>&, bool shuffle, uint32_t seed);
diff --git a/render-test/runner.cpp b/render-test/runner.cpp
index dd6aef3a54..54660979cf 100644
--- a/render-test/runner.cpp
+++ b/render-test/runner.cpp
@@ -18,6 +18,7 @@
#include <mapbox/pixelmatch.hpp>
+#include "allocation_index.hpp"
#include "metadata.hpp"
#include "parser.hpp"
#include "runner.hpp"
@@ -25,31 +26,79 @@
#include <algorithm>
#include <cassert>
#include <regex>
+#include <utility>
+#include <sstream>
+
+// static
+const std::string& TestRunner::getBasePath() {
+ const static std::string result =
+ std::string(TEST_RUNNER_ROOT_PATH).append("/mapbox-gl-js/test/integration/render-tests");
+ return result;
+}
+
+// static
+const std::vector<std::string>& TestRunner::getPlatformExpectationsPaths() {
+ // TODO: Populate from command line.
+ const static std::vector<std::string> result {
+ std::string(TEST_RUNNER_ROOT_PATH).append("/render-test/expected")
+ };
+ return result;
+}
-bool TestRunner::checkImage(mbgl::PremultipliedImage&& actual, TestMetadata& metadata) {
- const std::string base = metadata.path.remove_filename().string();
- metadata.actual = mbgl::encodePNG(actual);
+bool TestRunner::checkResults(mbgl::PremultipliedImage&& actualImage, TestMetadata& metadata) {
+ const std::string& base = metadata.paths.defaultExpectations();
+ const std::vector<mbgl::filesystem::path>& expectations = metadata.paths.expectations;
- if (actual.size.isEmpty()) {
+ metadata.actual = mbgl::encodePNG(actualImage);
+
+ if (actualImage.size.isEmpty()) {
metadata.errorMessage = "Invalid size for actual image";
return false;
}
#if !TEST_READ_ONLY
- if (getenv("UPDATE")) {
- mbgl::util::write_file(base + "/expected.png", mbgl::encodePNG(actual));
+ if (getenv("UPDATE_PLATFORM")) {
+ mbgl::filesystem::create_directories(expectations.back());
+ mbgl::util::write_file(expectations.back().string() + "/expected.png", mbgl::encodePNG(actualImage));
+ return true;
+ } else if (getenv("UPDATE_DEFAULT")) {
+ mbgl::util::write_file(base + "/expected.png", mbgl::encodePNG(actualImage));
return true;
+ } else if (getenv("UPDATE_METRICS")) {
+ if (!metadata.metrics.isEmpty()) {
+ mbgl::filesystem::create_directories(expectations.back());
+ mbgl::util::write_file(expectations.back().string() + "/metrics.json", serializeMetrics(metadata.metrics));
+ return true;
+ }
}
mbgl::util::write_file(base + "/actual.png", metadata.actual);
#endif
- mbgl::PremultipliedImage expected { actual.size };
- mbgl::PremultipliedImage diff { actual.size };
+ mbgl::PremultipliedImage expectedImage { actualImage.size };
+ mbgl::PremultipliedImage imageDiff { actualImage.size };
double pixels = 0.0;
+ std::vector<std::string> expectedImagesPaths;
+ mbgl::filesystem::path expectedMetricsPath;
+ for (auto rit = expectations.rbegin(); rit!= expectations.rend(); ++rit) {
+ if (mbgl::filesystem::exists(*rit)) {
+ if (metadata.expectedMetrics.isEmpty()) {
+ mbgl::filesystem::path maybeExpectedMetricsPath{ *rit };
+ maybeExpectedMetricsPath.replace_filename("metrics.json");
+ metadata.expectedMetrics = readExpectedMetrics(maybeExpectedMetricsPath);
+ }
+ expectedImagesPaths = readExpectedEntries(*rit);
+ if (!expectedImagesPaths.empty()) break;
+ }
+ }
- for (const auto& entry: readExpectedEntries(base)) {
+ if (expectedImagesPaths.empty()) {
+ metadata.errorMessage = "Failed to find expectations for: " + metadata.paths.stylePath.string();
+ return false;
+ }
+
+ for (const auto& entry: expectedImagesPaths) {
mbgl::optional<std::string> maybeExpectedImage = mbgl::util::readFile(entry);
if (!maybeExpectedImage) {
metadata.errorMessage = "Failed to load expected image " + entry;
@@ -58,23 +107,48 @@ bool TestRunner::checkImage(mbgl::PremultipliedImage&& actual, TestMetadata& met
metadata.expected = *maybeExpectedImage;
- expected = mbgl::decodeImage(*maybeExpectedImage);
+ expectedImage = mbgl::decodeImage(*maybeExpectedImage);
pixels = // implicitly converting from uint64_t
- mapbox::pixelmatch(actual.data.get(), expected.data.get(), expected.size.width,
- expected.size.height, diff.data.get(), 0.16); // GL JS uses 0.1285
+ mapbox::pixelmatch(actualImage.data.get(), expectedImage.data.get(), expectedImage.size.width,
+ expectedImage.size.height, imageDiff.data.get(), 0.1285); // Defined in GL JS
- metadata.diff = mbgl::encodePNG(diff);
+ metadata.diff = mbgl::encodePNG(imageDiff);
#if !TEST_READ_ONLY
mbgl::util::write_file(base + "/diff.png", metadata.diff);
#endif
- metadata.difference = pixels / expected.size.area();
+ metadata.difference = pixels / expectedImage.size.area();
if (metadata.difference <= metadata.allowed) {
break;
}
}
+ // Check memory metrics.
+ for (const auto& expected : metadata.expectedMetrics.memory) {
+ auto actual = metadata.metrics.memory.find(expected.first);
+ if (actual == metadata.metrics.memory.end()) {
+ metadata.errorMessage = "Failed to find memory probe: " + expected.first;
+ return false;
+ }
+ if (actual->second.peak > expected.second.peak) {
+ std::stringstream ss;
+ ss << "Allocated memory peak size at probe \"" << expected.first << "\" is "
+ << actual->second.peak << " bytes, expected is " << expected.second.peak << " bytes.";
+
+ metadata.errorMessage = ss.str();
+ return false;
+ }
+
+ if (actual->second.allocations > expected.second.allocations) {
+ std::stringstream ss;
+ ss << "Number of allocations at probe \"" << expected.first << "\" is "
+ << actual->second.allocations << ", expected is " << expected.second.allocations << ".";
+
+ metadata.errorMessage = ss.str();
+ return false;
+ }
+ }
return true;
}
@@ -120,6 +194,9 @@ bool TestRunner::runOperations(const std::string& key, TestMetadata& metadata) {
static const std::string removeSourceOp("removeSource");
static const std::string setPaintPropertyOp("setPaintProperty");
static const std::string setLayoutPropertyOp("setLayoutProperty");
+ static const std::string memoryProbeOp("probeMemory");
+ static const std::string memoryProbeStartOp("probeMemoryStart");
+ static const std::string memoryProbeEndOp("probeMemoryEnd");
// wait
if (operationArray[0].GetString() == waitOp) {
@@ -357,7 +434,25 @@ bool TestRunner::runOperations(const std::string& key, TestMetadata& metadata) {
const mbgl::JSValue* propertyValue = &operationArray[3];
layer->setLayoutProperty(propertyName, propertyValue);
}
-
+ // probeMemoryStart
+ } else if (operationArray[0].GetString() == memoryProbeStartOp) {
+ assert(!AllocationIndex::isActive());
+ AllocationIndex::setActive(true);
+ // probeMemory
+ } else if (operationArray[0].GetString() == memoryProbeOp) {
+ assert(AllocationIndex::isActive());
+ assert(operationArray.Size() >= 2u);
+ assert(operationArray[1].IsString());
+ std::string mark = std::string(operationArray[1].GetString(), operationArray[1].GetStringLength());
+
+ metadata.metrics.memory.emplace(std::piecewise_construct,
+ std::forward_as_tuple(std::move(mark)),
+ std::forward_as_tuple(AllocationIndex::getAllocatedSizePeak(), AllocationIndex::getAllocationsCount()));
+ // probeMemoryEnd
+ } else if (operationArray[0].GetString() == memoryProbeEndOp) {
+ assert(AllocationIndex::isActive());
+ AllocationIndex::setActive(false);
+ AllocationIndex::reset();
} else {
metadata.errorMessage = std::string("Unsupported operation: ") + operationArray[0].GetString();
return false;
@@ -379,6 +474,8 @@ TestRunner::Impl::Impl(const TestMetadata& metadata)
mbgl::ResourceOptions().withCacheOnlyRequestsSupport(false)) {}
bool TestRunner::run(TestMetadata& metadata) {
+ AllocationIndex::setActive(false);
+ AllocationIndex::reset();
std::string key = mbgl::util::toString(uint32_t(metadata.mapMode))
+ "/" + mbgl::util::toString(metadata.pixelRatio)
+ "/" + mbgl::util::toString(uint32_t(metadata.crossSourceCollisions));
@@ -410,7 +507,7 @@ bool TestRunner::run(TestMetadata& metadata) {
return false;
}
- return checkImage(std::move(image), metadata);
+ return checkResults(std::move(image), metadata);
}
void TestRunner::reset() {
diff --git a/render-test/runner.hpp b/render-test/runner.hpp
index cbc0f42546..fdea65e104 100644
--- a/render-test/runner.hpp
+++ b/render-test/runner.hpp
@@ -14,9 +14,14 @@ public:
bool run(TestMetadata&);
void reset();
+ /// Returns path of the render tests root directory.
+ static const std::string& getBasePath();
+ /// Returns path of mapbox-gl-native expectations directory.
+ static const std::vector<std::string>& getPlatformExpectationsPaths();
+
private:
bool runOperations(const std::string& key, TestMetadata&);
- bool checkImage(mbgl::PremultipliedImage&& image, TestMetadata&);
+ bool checkResults(mbgl::PremultipliedImage&& image, TestMetadata&);
struct Impl {
Impl(const TestMetadata&);
diff --git a/scripts/check-ci-job-skippability.js b/scripts/check-ci-job-skippability.js
new file mode 100755
index 0000000000..5629918791
--- /dev/null
+++ b/scripts/check-ci-job-skippability.js
@@ -0,0 +1,121 @@
+#!/usr/bin/env node
+
+/*
+This script takes two parameters or three environment variables:
+
+ {CI job name|$CIRCLE_JOB} {SHA1...SHA1|$CIRCLE_MERGE_BASE...$CIRCLE_SHA1}
+
+The flow of this script:
+
+ - List of files changed in PR: `git diff --name-only start...end`
+ - Extract list into array.
+ - Remove items from array if they match:
+ - Global ignore list.
+ - Other platform ignore lists.
+ - If array still contains items, do not authorize skipping CI job.
+ - Else if array is empty, skip CI job.
+
+To add support for a new platform:
+
+ - Add a new PLATFORM_FILES array with a list of unique paths for the
+ files belonging to the platform.
+ - Add a case to `skippableFilePatternsForCIJob()` with:
+ - The prefix for the platform's CI job names.
+ - A concatenated array of other platform paths that are safe to ignore
+ for the new platform. These should be files that can change without
+ having any effect on the output of the new platform.
+ - Update other cases in `skippableFilePatternsForCIJob()` to include the
+ paths for the new platform.
+*/
+
+const execSync = require('child_process').execSync;
+const _ = require('lodash');
+const minimatch = require("minimatch");
+
+console.step = _.partial(console.log, '\n\033[1m\033[36m*', _, '\033[0m');
+
+// BUILD PARAMETERS
+const CI_JOB_NAME = process.argv[2] || process.env.CIRCLE_JOB;
+if (!CI_JOB_NAME) { console.error('You must specify the name of a CI job as a parameter or environment variable.'); process.exit(1); }
+
+const COMMIT_RANGE = process.argv[3] || `${process.env.CIRCLE_MERGE_BASE}...${process.env.CIRCLE_SHA1}`;
+if (!COMMIT_RANGE || COMMIT_RANGE.includes('undefined')) { console.error('You must specify a range of commits to check for changed files in as a parameter or environment variable.'); process.exit(1); }
+
+// FILE SKIPPING PATTERNS
+const ALWAYS_SKIPPABLE_FILES = [
+ '**/CHANGELOG.md',
+ '**/README.md',
+ '**/CONTRIBUTING.md',
+ '**/DEVELOPING.md',
+ '**/INSTALL.md',
+ '**/*.podspec'
+]
+
+const ANDROID_FILES = [
+ 'platform/android/**'
+]
+
+const DARWIN_FILES = [
+ 'platform/darwin/**'
+]
+
+const IOS_FILES = [
+ 'platform/ios/**'
+]
+
+const MACOS_FILES = [
+ 'platform/macos/**'
+]
+
+const OTHER_PLATFORMS = [
+ 'platform/glfw/**',
+ 'platform/linux/**',
+ 'platform/node/**',
+ 'platform/qt/**',
+ 'benchmark/**',
+ 'test/**'
+]
+
+function skippableFilePatternsForCIJob(job) {
+ if (job.includes('render-tests')) {
+ // Render tests run on specific platforms are effectively unskippable core jobs, not platform jobs.
+ return ALWAYS_SKIPPABLE_FILES;
+ } else if (job.startsWith('ios')) {
+ return _.concat(ALWAYS_SKIPPABLE_FILES, ANDROID_FILES, MACOS_FILES, OTHER_PLATFORMS);
+ } else if (job.startsWith('android')) {
+ return _.concat(ALWAYS_SKIPPABLE_FILES, DARWIN_FILES, IOS_FILES, MACOS_FILES, OTHER_PLATFORMS);
+ } else if (job.startsWith('macos')) {
+ return _.concat(ALWAYS_SKIPPABLE_FILES, ANDROID_FILES, IOS_FILES, OTHER_PLATFORMS);
+ } else {
+ return ALWAYS_SKIPPABLE_FILES;
+ }
+}
+
+console.step(`Getting list of files changed between ${COMMIT_RANGE}`);
+
+let changedFiles = execSync(`git diff --name-only ${COMMIT_RANGE}`).toString().split('\n');
+changedFiles = _.compact(changedFiles);
+
+console.log(`${changedFiles.length} file${changedFiles.length == 1 ? '':'s'} changed.`);
+
+console.step(`Checking changed files for paths relevant to ${CI_JOB_NAME} job`);
+
+// Filter the changed files array to remove files that are irrelevant to the specified CI job.
+_.remove(changedFiles, function(changedFile) {
+ for (const skippableFilePattern of skippableFilePatternsForCIJob(CI_JOB_NAME)) {
+ if (minimatch(changedFile, skippableFilePattern, { dot: true, nocase: true })) {
+ return true;
+ }
+ }
+ return false;
+});
+
+if (changedFiles.length) {
+ console.log(`Found ${changedFiles.length} unskippable changed file${changedFiles.length == 1 ? '':'s'}:\n`, changedFiles);
+} else {
+ console.log(`Found no relevant changed files, so it's safe to skip the remainder of this CI job.`)
+ if (process.env.CIRCLECI) {
+ console.step('Aborting CI job');
+ execSync(`circleci step halt`, {stdio: 'inherit'});
+ }
+}
diff --git a/scripts/check-cxx11abi.dat b/scripts/check-cxx11abi.dat
deleted file mode 100755
index eab7298b50..0000000000
--- a/scripts/check-cxx11abi.dat
+++ /dev/null
Binary files differ
diff --git a/scripts/check-cxx11abi.sh b/scripts/check-cxx11abi.sh
deleted file mode 100755
index c6fd9258c5..0000000000
--- a/scripts/check-cxx11abi.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-if ! [ `uname -s` = 'Linux' ] || \
- ! command -v readelf > /dev/null || \
- ! command -v c++filt > /dev/null; then
- echo -n "OFF"
- exit 0
-fi
-
-# check-cxx11abi.dat is a binary just so we can use the loader
-# to take care of finding the libstdc++ which can be tricky.
-LIBSTDCPP=$(ldd $(dirname $0)/check-cxx11abi.dat |grep libstdc++ |cut -d' ' -f3)
-
-if [ $(readelf -Ws $LIBSTDCPP |c++filt |grep -c ::__cxx11::) -gt 0 ]; then
- echo -n "ON"
-else
- echo -n "OFF"
-fi
diff --git a/scripts/ci/Dockerfile b/scripts/ci/Dockerfile
new file mode 100644
index 0000000000..648581044f
--- /dev/null
+++ b/scripts/ci/Dockerfile
@@ -0,0 +1,110 @@
+FROM ubuntu:disco-20190718
+
+ENV DEBIAN_FRONTEND="noninteractive"
+ENV ANDROID_HOME="/opt/android"
+
+WORKDIR /src
+
+# Use faster mirrors from Finland
+COPY etc/apt/sources.list /etc/apt
+
+RUN set -eu \
+ && apt-get update\
+ && apt-get dist-upgrade -y
+
+# CI requirements
+RUN set -eu && apt-get install -y \
+ ca-certificates \
+ git \
+ gzip \
+ ssh \
+ tar
+
+# Base dependencies
+RUN set -eu && apt-get install -y \
+ ccache \
+ clang-8 \
+ clang-format-8 \
+ clang-tidy-8 \
+ cmake \
+ fonts-noto \
+ g++-8 \
+ libc++-8-dev \
+ libc++abi-8-dev \
+ mesa-common-dev \
+ ninja-build \
+ nodejs \
+ npm \
+ pkg-config \
+ python3-pip \
+ software-properties-common \
+ xvfb
+
+RUN pip3 install cmake_format
+
+# Linux dependencies
+RUN set -eu && apt-get install -y \
+ libcurl4-openssl-dev \
+ libgl1-mesa-dev \
+ libglfw3-dev \
+ libicu-dev \
+ libjpeg-turbo8-dev \
+ libpng-dev \
+ libuv1-dev \
+ zlib1g-dev
+
+# Qt dependencies
+RUN set -eu && apt-get install -y \
+ qdoc-qt5 \
+ qt5-default
+
+# Android dependencies
+RUN set -eu && apt-get install -y \
+ coreutils \
+ curl \
+ openjdk-8-jdk-headless \
+ unzip
+
+# Install old compilers
+RUN set -eu \
+ && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F \
+ && add-apt-repository "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu xenial main" \
+ && add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ xenial main universe" \
+ && apt-get install -y \
+ g++-4.9 \
+ g++-5
+
+# Install Android NDK
+RUN set -eu \
+ && mkdir -p ${ANDROID_HOME} && cd ${ANDROID_HOME} \
+ && curl -L --retry 3 https://dl.google.com/android/repository/android-ndk-r19-linux-x86_64.zip -o ndk.zip \
+ && (echo "f02ad84cb5b6e1ff3eea9e6168037c823408c8ac ndk.zip" | sha1sum -c) \
+ && unzip -q ndk.zip && rm ndk.zip && mv android-ndk-r* ndk-bundle
+
+
+RUN set -eu \
+ && cd ${ANDROID_HOME} \
+ && curl -L --retry 3 https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -o tools.zip \
+ && (echo "92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9 tools.zip" | sha256sum -c) \
+ && unzip -q tools.zip && rm tools.zip
+
+RUN set -eu \
+ && yes | ${ANDROID_HOME}/tools/bin/sdkmanager \
+ "platform-tools" \
+ "platforms;android-26" \
+ "build-tools;26.0.3" \
+ "platforms;android-27" \
+ "build-tools;27.0.3" \
+ "platforms;android-28" \
+ "build-tools;28.0.3" \
+ "extras;android;m2repository" \
+ "patcher;v4" \
+ "extras;google;m2repository" \
+ "extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2" \
+ "cmake;3.10.2.4988404"
+
+# Configure ccache
+RUN set -eu && /usr/sbin/update-ccache-symlinks
+
+# Cleanup
+RUN set -eu && apt-get clean
diff --git a/scripts/ci/etc/apt/sources.list b/scripts/ci/etc/apt/sources.list
new file mode 100644
index 0000000000..c5359e131a
--- /dev/null
+++ b/scripts/ci/etc/apt/sources.list
@@ -0,0 +1,10 @@
+deb http://fi.archive.ubuntu.com/ubuntu/ disco main restricted
+deb http://fi.archive.ubuntu.com/ubuntu/ disco-updates main restricted
+deb http://fi.archive.ubuntu.com/ubuntu/ disco universe
+deb http://fi.archive.ubuntu.com/ubuntu/ disco-updates universe
+deb http://fi.archive.ubuntu.com/ubuntu/ disco multiverse
+deb http://fi.archive.ubuntu.com/ubuntu/ disco-updates multiverse
+deb http://fi.archive.ubuntu.com/ubuntu/ disco-backports main restricted universe multiverse
+deb http://security.ubuntu.com/ubuntu/ disco-security main restricted
+deb http://security.ubuntu.com/ubuntu/ disco-security universe
+deb http://security.ubuntu.com/ubuntu/ disco-security multiverse
diff --git a/scripts/environment.js b/scripts/environment.js
index 0468be3720..759a7f2a42 100755
--- a/scripts/environment.js
+++ b/scripts/environment.js
@@ -23,7 +23,7 @@ if (pr) {
});
} else {
const head = process.env['CIRCLE_SHA1'];
- for (const sha of execSync(`git rev-list --max-count=10 ${head}`).toString().trim().split('\n')) {
+ for (const sha of execSync(`git rev-list --max-count=500 ${head}`).toString().trim().split('\n')) {
const base = execSync(`git branch -r --contains ${sha} origin/master origin/release-*`).toString().split('\n')[0].trim().replace(/^origin\//, '');
if (base) {
const mergeBase = execSync(`git merge-base origin/${base} ${head}`).toString().trim();
diff --git a/scripts/generate-file-lists.js b/scripts/generate-file-lists.js
index 2100928438..d1f5e77585 100755
--- a/scripts/generate-file-lists.js
+++ b/scripts/generate-file-lists.js
@@ -126,26 +126,31 @@ generateXcodeSourceList('platform/macos/macos.xcodeproj', 'dynamic', 'sdk');
generateXcodeSourceList('platform/ios/ios.xcodeproj', 'dynamic', 'sdk');
const vendorRegex = /^(?:(?:src|include)\/)?(?:(.+)\/)?[^\/]+$/
-generateFileList('vendor/args-files.json', [ 'vendor/args' ], vendorRegex, [ "args.hxx" ]);
generateFileList('vendor/boost-files.json', [ 'vendor/boost' ], vendorRegex, [ "include/**/*.hpp", "include/**/*.h" ]);
generateFileList('vendor/cheap-ruler-cpp-files.json', [ 'vendor/cheap-ruler-cpp' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/earcut.hpp-files.json', [ 'vendor/earcut.hpp' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/eternal-files.json', [ 'vendor/eternal' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/expected-files.json', [ 'vendor/expected' ], vendorRegex, [ "include/expected.hpp" ]);
-generateFileList('vendor/filesystem-files.json', [ 'vendor/filesystem' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/geojson-vt-cpp-files.json', [ 'vendor/geojson-vt-cpp' ], vendorRegex, [ "include/**/*.hpp" ]);
-generateFileList('vendor/geojson.hpp-files.json', [ 'vendor/geojson.hpp' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/icu-files.json', [ 'vendor/icu' ], vendorRegex, [ "include/**/*.h", "src/*.h", "src/*.cpp" ]);
-generateFileList('vendor/jni.hpp-files.json', [ 'vendor/jni.hpp' ], vendorRegex, [ "include/**/*.hpp", ":!:include/jni/string_conversion.hpp" ]);
-generateFileList('vendor/kdbush.hpp-files.json', [ 'vendor/kdbush.hpp' ], vendorRegex, [ "include/*.hpp" ]);
-generateFileList('vendor/mapbox-base-files.json', [ 'vendor/mapbox-base/libs/geometry.hpp', 'vendor/mapbox-base/libs/variant', 'vendor/mapbox-base/libs/optional' ], vendorRegex, [ "include/*.hpp", "include/**/*.hpp", "optional.hpp" ]);
-generateFileList('vendor/pixelmatch-cpp-files.json', [ 'vendor/pixelmatch-cpp' ], vendorRegex, [ "include/**/*.hpp" ]);
+generateFileList('vendor/mapbox-base-files.json',
+ [ 'vendor/mapbox-base/extras/kdbush.hpp',
+ 'vendor/mapbox-base/extras/filesystem',
+ 'vendor/mapbox-base/extras/rapidjson',
+ 'vendor/mapbox-base/mapbox/pixelmatch-cpp',
+ 'vendor/mapbox-base/mapbox/geometry.hpp',
+ 'vendor/mapbox-base/mapbox/variant',
+ 'vendor/mapbox-base/mapbox/optional',
+ 'vendor/mapbox-base/mapbox/supercluster.hpp',
+ 'vendor/mapbox-base/mapbox/geojson.hpp',
+ 'vendor/mapbox-base/mapbox/jni.hpp',
+ 'vendor/mapbox-base/mapbox/weak',
+ 'vendor/mapbox-base/mapbox/typewrapper' ],
+ vendorRegex, [ "include/*.hpp", "include/**/*.hpp", "include/**/*.h", "optional.hpp", ":!:include/jni/string_conversion.hpp" ]);
generateFileList('vendor/polylabel-files.json', [ 'vendor/polylabel' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/protozero-files.json', [ 'vendor/protozero' ], vendorRegex, [ "include/**/*.hpp" ]);
-generateFileList('vendor/rapidjson-files.json', [ 'vendor/rapidjson' ], vendorRegex, [ "include/**/*.h" ]);
generateFileList('vendor/shelf-pack-cpp-files.json', [ 'vendor/shelf-pack-cpp' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/sqlite-files.json', [ 'vendor/sqlite' ], vendorRegex, [ "include/*.h", "src/*.c" ]);
-generateFileList('vendor/supercluster.hpp-files.json', [ 'vendor/supercluster.hpp' ], vendorRegex, [ "include/*.hpp" ]);
generateFileList('vendor/unique_resource-files.json', [ 'vendor/unique_resource' ], vendorRegex, [ "unique_resource.hpp" ]);
generateFileList('vendor/vector-tile-files.json', [ 'vendor/vector-tile' ], vendorRegex, [ "include/**/*.hpp" ]);
generateFileList('vendor/wagyu-files.json', [ 'vendor/wagyu' ], vendorRegex, [ "include/**/*.hpp" ]);
diff --git a/src/core-files.json b/src/core-files.json
index f2da5a3d05..f4f2dd2c40 100644
--- a/src/core-files.json
+++ b/src/core-files.json
@@ -456,12 +456,14 @@
"mbgl/tile/tile_necessity.hpp": "include/mbgl/tile/tile_necessity.hpp",
"mbgl/util/async_request.hpp": "include/mbgl/util/async_request.hpp",
"mbgl/util/async_task.hpp": "include/mbgl/util/async_task.hpp",
+ "mbgl/util/bitmask_operations.hpp": "include/mbgl/util/bitmask_operations.hpp",
"mbgl/util/char_array_buffer.hpp": "include/mbgl/util/char_array_buffer.hpp",
"mbgl/util/chrono.hpp": "include/mbgl/util/chrono.hpp",
"mbgl/util/color.hpp": "include/mbgl/util/color.hpp",
"mbgl/util/compression.hpp": "include/mbgl/util/compression.hpp",
"mbgl/util/constants.hpp": "include/mbgl/util/constants.hpp",
"mbgl/util/convert.hpp": "include/mbgl/util/convert.hpp",
+ "mbgl/util/default_styles.hpp": "include/mbgl/util/default_styles.hpp",
"mbgl/util/enum.hpp": "include/mbgl/util/enum.hpp",
"mbgl/util/event.hpp": "include/mbgl/util/event.hpp",
"mbgl/util/exception.hpp": "include/mbgl/util/exception.hpp",
@@ -479,7 +481,6 @@
"mbgl/util/logging.hpp": "include/mbgl/util/logging.hpp",
"mbgl/util/noncopyable.hpp": "include/mbgl/util/noncopyable.hpp",
"mbgl/util/optional.hpp": "include/mbgl/util/optional.hpp",
- "mbgl/util/peer.hpp": "include/mbgl/util/peer.hpp",
"mbgl/util/platform.hpp": "include/mbgl/util/platform.hpp",
"mbgl/util/premultiply.hpp": "include/mbgl/util/premultiply.hpp",
"mbgl/util/projection.hpp": "include/mbgl/util/projection.hpp",
diff --git a/src/mbgl/annotation/annotation_source.hpp b/src/mbgl/annotation/annotation_source.hpp
index 0728f3207e..018e2136ea 100644
--- a/src/mbgl/annotation/annotation_source.hpp
+++ b/src/mbgl/annotation/annotation_source.hpp
@@ -5,17 +5,22 @@
namespace mbgl {
-class AnnotationSource : public style::Source {
+class AnnotationSource final : public style::Source {
public:
AnnotationSource();
class Impl;
const Impl& impl() const;
+ mapbox::base::WeakPtr<Source> makeWeakPtr() override {
+ return weakFactory.makeWeakPtr();
+ }
+
private:
void loadDescription(FileSource&) final;
Mutable<Impl> mutableImpl() const;
+ mapbox::base::WeakPtrFactory<Source> weakFactory {this};
};
class AnnotationSource::Impl : public style::Source::Impl {
diff --git a/src/mbgl/geometry/dem_data.cpp b/src/mbgl/geometry/dem_data.cpp
index 92dd7aee26..79998a9c43 100644
--- a/src/mbgl/geometry/dem_data.cpp
+++ b/src/mbgl/geometry/dem_data.cpp
@@ -3,36 +3,23 @@
namespace mbgl {
-DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding encoding):
+DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding _encoding):
dim(_image.size.height),
// extra two pixels per row for border backfilling on either edge
stride(dim + 2),
+ encoding(_encoding),
image({ static_cast<uint32_t>(stride), static_cast<uint32_t>(stride) }) {
if (_image.size.height != _image.size.width){
throw std::runtime_error("raster-dem tiles must be square.");
}
- auto decodeMapbox = [] (const uint8_t r, const uint8_t g, const uint8_t b){
- // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
- return (r * 256 * 256 + g * 256 + b)/10 - 10000;
- };
-
- auto decodeTerrarium = [] (const uint8_t r, const uint8_t g, const uint8_t b){
- // https://aws.amazon.com/public-datasets/terrain/
- return ((r * 256 + g + b / 256) - 32768);
- };
-
- auto decodeRGB = encoding == Tileset::DEMEncoding::Terrarium ? decodeTerrarium : decodeMapbox;
-
- std::memset(image.data.get(), 0, image.bytes());
-
+ auto* dest = reinterpret_cast<uint32_t*>(image.data.get()) + stride + 1;
+ auto* source = reinterpret_cast<uint32_t*>(_image.data.get());
for (int32_t y = 0; y < dim; y++) {
- for (int32_t x = 0; x < dim; x++) {
- const int32_t i = y * dim + x;
- const int32_t j = i * 4;
- set(x, y, decodeRGB(_image.data[j], _image.data[j+1], _image.data[j+2]));
- }
+ memcpy(dest, source, dim * 4);
+ dest += stride;
+ source += dim;
}
// in order to avoid flashing seams between tiles, here we are initially populating a 1px border of
@@ -40,25 +27,20 @@ DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding encoding
// replaced when the tile's neighboring tiles are loaded and the accurate data can be backfilled using
// DEMData#backfillBorder
+ auto* data = reinterpret_cast<uint32_t*>(image.data.get());
for (int32_t x = 0; x < dim; x++) {
+ auto rowOffset = stride * (x + 1);
// left vertical border
- set(-1, x, get(0, x));
+ data[rowOffset] = data[rowOffset + 1];
// right vertical border
- set(dim, x, get(dim - 1, x));
-
- //left horizontal border
- set(x, -1, get(x, 0));
-
- // right horizontal border
- set(x, dim, get(x, dim - 1));
+ data[rowOffset + dim + 1] = data[rowOffset + dim];
}
-
- // corners
- set(-1, -1, get(0, 0));
- set(dim, -1, get(dim - 1, 0));
- set( -1, dim, get(0, dim - 1));
- set(dim, dim, get(dim - 1, dim - 1));
+
+ // top horizontal border with corners
+ memcpy(data, data + stride, stride * 4);
+ // bottom horizontal border with corners
+ memcpy(data + (dim + 1) * stride, data + dim * stride, stride * 4);
}
// This function takes the DEMData from a neighboring tile and backfills the edge/corner
@@ -90,11 +72,29 @@ void DEMData::backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy
int32_t ox = -dx * dim;
int32_t oy = -dy * dim;
+ auto* dest = reinterpret_cast<uint32_t*>(image.data.get());
+ auto* source = reinterpret_cast<uint32_t*>(o.image.data.get());
+
for (int32_t y = yMin; y < yMax; y++) {
for (int32_t x = xMin; x < xMax; x++) {
- set(x, y, o.get(x + ox, y + oy));
+ dest[idx(x, y)] = source[idx(x + ox, y + oy)];
}
}
}
+int32_t DEMData::get(const int32_t x, const int32_t y) const {
+ const auto& unpack = getUnpackVector();
+ const uint8_t* value = image.data.get() + idx(x, y) * 4;
+ return value[0] * unpack[0] + value[1] * unpack[1] + value[2] * unpack[2] - unpack[3];
+}
+
+const std::array<float, 4>& DEMData::getUnpackVector() const {
+ // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
+ static const std::array<float, 4> unpackMapbox = {{ 6553.6, 25.6, 0.1, 10000.0 }};
+ // https://aws.amazon.com/public-datasets/terrain/
+ static const std::array<float, 4> unpackTerrarium = {{ 256.0, 1.0, 1.0 / 256.0, 32768.0 }};
+
+ return encoding == Tileset::DEMEncoding::Terrarium ? unpackTerrarium : unpackMapbox;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/geometry/dem_data.hpp b/src/mbgl/geometry/dem_data.hpp
index 21146bc468..537575873f 100644
--- a/src/mbgl/geometry/dem_data.hpp
+++ b/src/mbgl/geometry/dem_data.hpp
@@ -16,13 +16,8 @@ public:
DEMData(const PremultipliedImage& image, Tileset::DEMEncoding encoding);
void backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy);
- void set(const int32_t x, const int32_t y, const int32_t value) {
- reinterpret_cast<int32_t*>(image.data.get())[idx(x, y)] = value + 65536;
- }
-
- int32_t get(const int32_t x, const int32_t y) const {
- return reinterpret_cast<const int32_t*>(image.data.get())[idx(x, y)] - 65536;
- }
+ int32_t get(const int32_t x, const int32_t y) const;
+ const std::array<float, 4>& getUnpackVector() const;
const PremultipliedImage* getImage() const {
return &image;
@@ -32,16 +27,17 @@ public:
const int32_t stride;
- private:
- PremultipliedImage image;
+private:
+ Tileset::DEMEncoding encoding;
+ PremultipliedImage image;
- size_t idx(const int32_t x, const int32_t y) const {
- assert(x >= -1);
- assert(x < dim + 1);
- assert(y >= -1);
- assert(y < dim + 1);
- return (y + 1) * stride + (x + 1);
- }
+ size_t idx(const int32_t x, const int32_t y) const {
+ assert(x >= -1);
+ assert(x < dim + 1);
+ assert(y >= -1);
+ assert(y < dim + 1);
+ return (y + 1) * stride + (x + 1);
+ }
};
diff --git a/src/mbgl/layout/symbol_feature.hpp b/src/mbgl/layout/symbol_feature.hpp
index ed9c0783d0..03d8e5018d 100644
--- a/src/mbgl/layout/symbol_feature.hpp
+++ b/src/mbgl/layout/symbol_feature.hpp
@@ -32,6 +32,7 @@ public:
optional<std::string> icon;
float sortKey = 0.0f;
std::size_t index;
+ bool allowsVerticalWritingMode = false;
};
} // namespace mbgl
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index 133cdec698..41a00e0131 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -21,24 +21,28 @@ const Shaping& getAnyShaping(const ShapedTextOrientations& shapedTextOrientation
SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
+ const optional<PositionedIcon>& verticallyShapedIcon,
const style::SymbolLayoutProperties::Evaluated& layout,
- const float layoutTextSize,
const style::SymbolPlacementType textPlacement,
const std::array<float, 2>& textOffset,
- const GlyphPositions& positions) : line(std::move(line_)) {
+ const GlyphPositions& positions,
+ bool allowVerticalPlacement) : line(std::move(line_)) {
// Create the quads used for rendering the icon and glyphs.
if (shapedIcon) {
- iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.horizontal);
+ iconQuad = getIconQuad(*shapedIcon, getAnyShaping(shapedTextOrientations).writingMode);
+ if (verticallyShapedIcon) {
+ verticalIconQuad = getIconQuad(*verticallyShapedIcon, shapedTextOrientations.vertical.writingMode);
+ }
}
bool singleLineInitialized = false;
const auto initHorizontalGlyphQuads = [&] (SymbolQuads& quads, const Shaping& shaping) {
if (!shapedTextOrientations.singleLine) {
- quads = getGlyphQuads(shaping, textOffset, layout, textPlacement, positions);
+ quads = getGlyphQuads(shaping, textOffset, layout, textPlacement, positions, allowVerticalPlacement);
return;
}
if (!singleLineInitialized) {
- rightJustifiedGlyphQuads = getGlyphQuads(shaping, textOffset, layout, textPlacement, positions);
+ rightJustifiedGlyphQuads = getGlyphQuads(shaping, textOffset, layout, textPlacement, positions, allowVerticalPlacement);
singleLineInitialized = true;
}
};
@@ -56,7 +60,7 @@ SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
}
if (shapedTextOrientations.vertical) {
- verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.vertical, textOffset, layout, textPlacement, positions);
+ verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.vertical, textOffset, layout, textPlacement, positions, allowVerticalPlacement);
}
}
@@ -68,6 +72,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
std::shared_ptr<SymbolInstanceSharedData> sharedData_,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
+ const optional<PositionedIcon>& verticallyShapedIcon,
const float textBoxScale_,
const float textPadding,
const SymbolPlacementType textPlacement,
@@ -80,17 +85,18 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
const std::size_t dataFeatureIndex_,
std::u16string key_,
const float overscaling,
- const float rotate,
- float radialTextOffset_) :
+ const float iconRotation,
+ const float textRotation,
+ const std::array<float, 2>& variableTextOffset_,
+ bool allowVerticalPlacement,
+ const SymbolContent iconType) :
sharedData(std::move(sharedData_)),
anchor(anchor_),
- // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap
- hasText(!sharedData->empty()),
- hasIcon(shapedIcon),
+ symbolContent(iconType),
// Create the collision features that will be used to check whether this symbol instance can be placed
// As a collision approximation, we can use either the vertical or any of the horizontal versions of the feature
- textCollisionFeature(sharedData->line, anchor, getAnyShaping(shapedTextOrientations), textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, rotate),
- iconCollisionFeature(sharedData->line, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature, rotate),
+ textCollisionFeature(sharedData->line, anchor, getAnyShaping(shapedTextOrientations), textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, textRotation),
+ iconCollisionFeature(sharedData->line, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature, iconRotation),
writingModes(WritingModeType::None),
layoutFeatureIndex(layoutFeatureIndex_),
dataFeatureIndex(dataFeatureIndex_),
@@ -98,8 +104,22 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
iconOffset(iconOffset_),
key(std::move(key_)),
textBoxScale(textBoxScale_),
- radialTextOffset(radialTextOffset_),
+ variableTextOffset(variableTextOffset_),
singleLine(shapedTextOrientations.singleLine) {
+ // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap
+ if(!sharedData->empty()) symbolContent |= SymbolContent::Text;
+ if (allowVerticalPlacement && shapedTextOrientations.vertical) {
+ const float verticalPointLabelAngle = 90.0f;
+ verticalTextCollisionFeature = CollisionFeature(line(), anchor, shapedTextOrientations.vertical, textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, textRotation + verticalPointLabelAngle);
+ if (verticallyShapedIcon) {
+ verticalIconCollisionFeature = CollisionFeature(sharedData->line,
+ anchor,
+ verticallyShapedIcon,
+ iconBoxScale, iconPadding,
+ indexedFeature,
+ iconRotation + verticalPointLabelAngle);
+ }
+ }
rightJustifiedGlyphQuadsSize = sharedData->rightJustifiedGlyphQuads.size();
centerJustifiedGlyphQuadsSize = sharedData->centerJustifiedGlyphQuads.size();
@@ -144,6 +164,23 @@ const optional<SymbolQuad>& SymbolInstance::iconQuad() const {
assert(sharedData);
return sharedData->iconQuad;
}
+
+bool SymbolInstance::hasText() const {
+ return static_cast<bool>(symbolContent & SymbolContent::Text);
+}
+
+bool SymbolInstance::hasIcon() const {
+ return static_cast<bool>(symbolContent & SymbolContent::IconRGBA) || hasSdfIcon();
+}
+
+bool SymbolInstance::hasSdfIcon() const {
+ return static_cast<bool>(symbolContent & SymbolContent::IconSDF);
+}
+
+const optional<SymbolQuad>& SymbolInstance::verticalIconQuad() const {
+ assert(sharedData);
+ return sharedData->verticalIconQuad;
+}
void SymbolInstance::releaseSharedData() {
sharedData.reset();
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index 48bb2f0cbc..4a57b527f7 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -4,7 +4,7 @@
#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/text/collision_feature.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
-
+#include <mbgl/util/bitmask_operations.hpp>
namespace mbgl {
@@ -25,11 +25,12 @@ struct SymbolInstanceSharedData {
SymbolInstanceSharedData(GeometryCoordinates line,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
+ const optional<PositionedIcon>& verticallyShapedIcon,
const style::SymbolLayoutProperties::Evaluated& layout,
- const float layoutTextSize,
const style::SymbolPlacementType textPlacement,
const std::array<float, 2>& textOffset,
- const GlyphPositions& positions);
+ const GlyphPositions& positions,
+ bool allowVerticalPlacement);
bool empty() const;
GeometryCoordinates line;
// Note: When singleLine == true, only `rightJustifiedGlyphQuads` is populated.
@@ -38,6 +39,14 @@ struct SymbolInstanceSharedData {
SymbolQuads leftJustifiedGlyphQuads;
SymbolQuads verticalGlyphQuads;
optional<SymbolQuad> iconQuad;
+ optional<SymbolQuad> verticalIconQuad;
+};
+
+enum class SymbolContent : uint8_t {
+ None = 0,
+ Text = 1 << 0,
+ IconRGBA = 1 << 1,
+ IconSDF = 1 << 2
};
class SymbolInstance {
@@ -46,6 +55,7 @@ public:
std::shared_ptr<SymbolInstanceSharedData> sharedData,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
+ const optional<PositionedIcon>& verticallyShapedIcon,
const float textBoxScale,
const float textPadding,
const style::SymbolPlacementType textPlacement,
@@ -58,8 +68,11 @@ public:
const std::size_t dataFeatureIndex,
std::u16string key,
const float overscaling,
- const float rotate,
- float radialTextOffset);
+ const float iconRotation,
+ const float textRotation,
+ const std::array<float, 2>& variableTextOffset,
+ bool allowVerticalPlacement,
+ const SymbolContent iconType = SymbolContent::None);
optional<size_t> getDefaultHorizontalPlacedTextIndex() const;
const GeometryCoordinates& line() const;
@@ -67,7 +80,11 @@ public:
const SymbolQuads& leftJustifiedGlyphQuads() const;
const SymbolQuads& centerJustifiedGlyphQuads() const;
const SymbolQuads& verticalGlyphQuads() const;
+ bool hasText() const;
+ bool hasIcon() const;
+ bool hasSdfIcon() const;
const optional<SymbolQuad>& iconQuad() const;
+ const optional<SymbolQuad>& verticalIconQuad() const;
void releaseSharedData();
private:
@@ -75,8 +92,7 @@ private:
public:
Anchor anchor;
- bool hasText;
- bool hasIcon;
+ SymbolContent symbolContent;
std::size_t rightJustifiedGlyphQuadsSize;
std::size_t centerJustifiedGlyphQuadsSize;
@@ -85,6 +101,8 @@ public:
CollisionFeature textCollisionFeature;
CollisionFeature iconCollisionFeature;
+ optional<CollisionFeature> verticalTextCollisionFeature = nullopt;
+ optional<CollisionFeature> verticalIconCollisionFeature = nullopt;
WritingModeType writingModes;
std::size_t layoutFeatureIndex; // Index into the set of features included at layout time
std::size_t dataFeatureIndex; // Index into the underlying tile data feature set
@@ -97,8 +115,9 @@ public:
optional<size_t> placedLeftTextIndex;
optional<size_t> placedVerticalTextIndex;
optional<size_t> placedIconIndex;
+ optional<size_t> placedVerticalIconIndex;
float textBoxScale;
- float radialTextOffset;
+ std::array<float, 2> variableTextOffset;
bool singleLine;
uint32_t crossTileID = 0;
};
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index d269ca4144..1dbb5d91dc 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -91,11 +91,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
pixelRatio(parameters.pixelRatio),
tileSize(util::tileSize * overscaling),
tilePixelRatio(float(util::EXTENT) / tileSize),
- textSize(toSymbolLayerProperties(layers.at(0)).layerImpl().layout.get<TextSize>()),
- iconSize(toSymbolLayerProperties(layers.at(0)).layerImpl().layout.get<IconSize>()),
layout(createLayout(toSymbolLayerProperties(layers.at(0)).layerImpl().layout, zoom)) {
const SymbolLayer::Impl& leader = toSymbolLayerProperties(layers.at(0)).layerImpl();
+ textSize = leader.layout.get<TextSize>();
+ iconSize = leader.layout.get<IconSize>();
+ textRadialOffset = leader.layout.get<TextRadialOffset>();
+
const bool hasText = has<TextField>(*layout) && has<TextFont>(*layout);
const bool hasIcon = has<IconImage>(*layout);
@@ -109,6 +111,19 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
const bool zOrderByViewportY = symbolZOrder == SymbolZOrderType::ViewportY || (symbolZOrder == SymbolZOrderType::Auto && !sortFeaturesByKey);
sortFeaturesByY = zOrderByViewportY && (layout->get<TextAllowOverlap>() || layout->get<IconAllowOverlap>() ||
layout->get<TextIgnorePlacement>() || layout->get<IconIgnorePlacement>());
+ if (layout->get<SymbolPlacement>() == SymbolPlacementType::Point) {
+ auto modes = layout->get<TextWritingMode>();
+ // Remove duplicates and preserve order.
+ std::set<style::TextWritingModeType> seen;
+ auto end = std::remove_if(modes.begin(),
+ modes.end(),
+ [&seen, this](const auto& placementMode) {
+ allowVerticalPlacement = allowVerticalPlacement || placementMode == style::TextWritingModeType::Vertical;
+ return !seen.insert(placementMode).second;
+ });
+ modes.erase(end, modes.end());
+ placementModes = std::move(modes);
+ }
for (const auto& layer : layers) {
layerPaintProperties.emplace(layer->baseImpl->id, layer);
@@ -148,7 +163,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
const bool canVerticalizeText = layout->get<TextRotationAlignment>() == AlignmentType::Map
&& layout->get<SymbolPlacement>() != SymbolPlacementType::Point
- && util::i18n::allowsVerticalWritingMode(ft.formattedText->rawText());
+ && ft.formattedText->allowsVerticalWritingMode();
// Loop through all characters of this text and collect unique codepoints.
for (std::size_t j = 0; j < ft.formattedText->length(); j++) {
@@ -156,7 +171,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
GlyphIDs& dependencies = glyphDependencies[sectionFontStack ? *sectionFontStack : baseFontStack];
char16_t codePoint = ft.formattedText->getCharCodeAt(j);
dependencies.insert(codePoint);
- if (canVerticalizeText) {
+ if (canVerticalizeText || (allowVerticalPlacement && ft.formattedText->allowsVerticalWritingMode())) {
if (char16_t verticalChr = util::i18n::verticalizePunctuation(codePoint)) {
dependencies.insert(verticalChr);
}
@@ -222,11 +237,9 @@ Shaping& shapingForTextJustifyType(ShapedTextOrientations& shapedTextOrientation
}
}
-} // namespace
-
-// static
-Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float radialOffset) {
- Point<float> result{};
+std::array<float, 2> evaluateRadialOffset(style::SymbolAnchorType anchor, float radialOffset) {
+ std::array<float, 2> result{{0.0f, 0.0f}};
+ if (radialOffset < 0.0f) radialOffset = 0.0f; // Ignore negative offset.
// solve for r where r^2 + r^2 = radialOffset^2
const float sqrt2 = 1.41421356237f;
const float hypotenuse = radialOffset / sqrt2;
@@ -234,17 +247,17 @@ Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float r
switch (anchor) {
case SymbolAnchorType::TopRight:
case SymbolAnchorType::TopLeft:
- result.y = hypotenuse - baselineOffset;
+ result[1] = hypotenuse - baselineOffset;
break;
case SymbolAnchorType::BottomRight:
case SymbolAnchorType::BottomLeft:
- result.y = -hypotenuse + baselineOffset;
+ result[1] = -hypotenuse + baselineOffset;
break;
case SymbolAnchorType::Bottom:
- result.y = -radialOffset + baselineOffset;
+ result[1] = -radialOffset + baselineOffset;
break;
case SymbolAnchorType::Top:
- result.y = radialOffset - baselineOffset;
+ result[1] = radialOffset - baselineOffset;
break;
default:
break;
@@ -253,17 +266,17 @@ Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float r
switch (anchor) {
case SymbolAnchorType::TopRight:
case SymbolAnchorType::BottomRight:
- result.x = -hypotenuse;
+ result[0] = -hypotenuse;
break;
case SymbolAnchorType::TopLeft:
case SymbolAnchorType::BottomLeft:
- result.x = hypotenuse;
+ result[0] = hypotenuse;
break;
case SymbolAnchorType::Left:
- result.x = radialOffset;
+ result[0] = radialOffset;
break;
case SymbolAnchorType::Right:
- result.x = -radialOffset;
+ result[0] = -radialOffset;
break;
default:
break;
@@ -272,6 +285,54 @@ Point<float> SymbolLayout::evaluateRadialOffset(SymbolAnchorType anchor, float r
return result;
}
+} // namespace
+
+// static
+std::array<float, 2> SymbolLayout::evaluateVariableOffset(style::SymbolAnchorType anchor, std::array<float, 2> offset) {
+ if (offset[1] == INVALID_OFFSET_VALUE) {
+ return evaluateRadialOffset(anchor, offset[0]);
+ }
+ std::array<float, 2> result{{0.0f, 0.0f}};
+ offset[0] = std::abs(offset[0]);
+ offset[1] = std::abs(offset[1]);
+
+ switch (anchor) {
+ case SymbolAnchorType::TopRight:
+ case SymbolAnchorType::TopLeft:
+ case SymbolAnchorType::Top:
+ result[1] = offset[1] - baselineOffset;
+ break;
+ case SymbolAnchorType::BottomRight:
+ case SymbolAnchorType::BottomLeft:
+ case SymbolAnchorType::Bottom:
+ result[1] = -offset[1] + baselineOffset;
+ break;
+ case SymbolAnchorType::Center:
+ case SymbolAnchorType::Left:
+ case SymbolAnchorType::Right:
+ break;
+ }
+
+ switch (anchor) {
+ case SymbolAnchorType::TopRight:
+ case SymbolAnchorType::BottomRight:
+ case SymbolAnchorType::Right:
+ result[0] = -offset[0];
+ break;
+ case SymbolAnchorType::TopLeft:
+ case SymbolAnchorType::BottomLeft:
+ case SymbolAnchorType::Left:
+ result[0] = offset[0];
+ break;
+ case SymbolAnchorType::Center:
+ case SymbolAnchorType::Top:
+ case SymbolAnchorType::Bottom:
+ break;
+ }
+
+ return result;
+}
+
void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
const ImageMap& imageMap, const ImagePositions& imagePositions) {
const bool isPointPlacement = layout->get<SymbolPlacement>() == SymbolPlacementType::Point;
@@ -283,7 +344,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
ShapedTextOrientations shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
- Point<float> textOffset;
+ std::array<float, 2> textOffset{{0.0f, 0.0f}};
// if feature has text, shape the text
if (feature.formattedText) {
@@ -301,27 +362,39 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
/* translate */ textOffset,
/* writingMode */ writingMode,
/* bidirectional algorithm object */ bidi,
- /* glyphs */ glyphMap);
+ /* glyphs */ glyphMap,
+ allowVerticalPlacement);
return result;
};
const std::vector<style::TextVariableAnchorType> variableTextAnchor = layout->evaluate<TextVariableAnchor>(zoom, feature);
- const float radialOffset = layout->evaluate<TextRadialOffset>(zoom, feature);
const SymbolAnchorType textAnchor = layout->evaluate<TextAnchor>(zoom, feature);
if (variableTextAnchor.empty()) {
// Layers with variable anchors use the `text-radial-offset` property and the [x, y] offset vector
// is calculated at placement time instead of layout time
+ const float radialOffset = layout->evaluate<TextRadialOffset>(zoom, feature);
if (radialOffset > 0.0f) {
// The style spec says don't use `text-offset` and `text-radial-offset` together
// but doesn't actually specify what happens if you use both. We go with the radial offset.
textOffset = evaluateRadialOffset(textAnchor, radialOffset * util::ONE_EM);
} else {
- textOffset = { layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
- layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM};
+ textOffset = {{layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
+ layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM}};
}
}
TextJustifyType textJustify = textAlongLine ? TextJustifyType::Center : layout->evaluate<TextJustify>(zoom, feature);
- // If this layer uses text-variable-anchor, generate shapings for all justification possibilities.
+
+ const auto addVerticalShapingForPointLabelIfNeeded = [&] {
+ if (allowVerticalPlacement && feature.formattedText->allowsVerticalWritingMode()) {
+ feature.formattedText->verticalizePunctuation();
+ // Vertical POI label placement is meant to be used for scripts that support vertical
+ // writing mode, thus, default style::TextJustifyType::Left justification is used. If Latin
+ // scripts would need to be supported, this should take into account other justifications.
+ shapedTextOrientations.vertical = applyShaping(*feature.formattedText, WritingModeType::Vertical, textAnchor, style::TextJustifyType::Left);
+ }
+ };
+
+ // If this layer uses text-variable-anchor, generate shapings for all justification possibilities.
if (!textAlongLine && !variableTextAnchor.empty()) {
std::vector<TextJustifyType> justifications;
if (textJustify != TextJustifyType::Auto) {
@@ -341,22 +414,31 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
Shaping shaping = applyShaping(*feature.formattedText, WritingModeType::Horizontal, SymbolAnchorType::Center, justification);
if (shaping) {
shapingForJustification = std::move(shaping);
- if (shaping.lineCount == 1u) {
+ if (shapingForJustification.lineCount == 1u) {
shapedTextOrientations.singleLine = true;
break;
}
}
}
+
+ // Vertical point label shaping if allowVerticalPlacement is enabled.
+ addVerticalShapingForPointLabelIfNeeded();
} else {
if (textJustify == TextJustifyType::Auto) {
textJustify = getAnchorJustification(textAnchor);
}
+
+ // Horizontal point or line label.
Shaping shaping = applyShaping(*feature.formattedText, WritingModeType::Horizontal, textAnchor, textJustify);
if (shaping) {
shapedTextOrientations.horizontal = std::move(shaping);
}
- if (util::i18n::allowsVerticalWritingMode(feature.formattedText->rawText()) && textAlongLine) {
+ // Vertical point label shaping if allowVerticalPlacement is enabled.
+ addVerticalShapingForPointLabelIfNeeded();
+
+ // Verticalized line label.
+ if (textAlongLine && feature.formattedText->allowsVerticalWritingMode()) {
feature.formattedText->verticalizePunctuation();
shapedTextOrientations.vertical = applyShaping(*feature.formattedText, WritingModeType::Vertical, textAnchor, textJustify);
}
@@ -364,16 +446,18 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
}
// if feature has icon, get sprite atlas position
+ SymbolContent iconType{SymbolContent::None};
if (feature.icon) {
auto image = imageMap.find(*feature.icon);
if (image != imageMap.end()) {
+ iconType = SymbolContent::IconRGBA;
shapedIcon = PositionedIcon::shapeIcon(
imagePositions.at(*feature.icon),
layout->evaluate<IconOffset>(zoom, feature),
layout->evaluate<IconAnchor>(zoom, feature),
layout->evaluate<IconRotate>(zoom, feature) * util::DEG2RAD);
if (image->second->sdf) {
- sdfIcons = true;
+ iconType = SymbolContent::IconSDF;
}
if (image->second->pixelRatio != pixelRatio) {
iconsNeedLinear = true;
@@ -385,7 +469,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
// if either shapedText or icon position is present, add the feature
if (getDefaultHorizontalShaping(shapedTextOrientations) || shapedIcon) {
- addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositions, textOffset);
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, std::move(shapedIcon), glyphPositions, textOffset, iconType);
}
feature.geometry.clear();
@@ -397,15 +481,15 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const SymbolFeature& feature,
const ShapedTextOrientations& shapedTextOrientations,
- const optional<PositionedIcon>& shapedIcon,
+ optional<PositionedIcon> shapedIcon,
const GlyphPositions& glyphPositions,
- Point<float> offset) {
+ std::array<float, 2> textOffset,
+ const SymbolContent iconType) {
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 = {{ offset.x, offset.y }};
const std::array<float, 2> iconOffset = layout->evaluate<IconOffset>(zoom, feature);
@@ -423,8 +507,17 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float textPadding = layout->get<TextPadding>() * tilePixelRatio;
const float iconPadding = layout->get<IconPadding>() * tilePixelRatio;
const float textMaxAngle = layout->get<TextMaxAngle>() * util::DEG2RAD;
- const float rotation = layout->evaluate<IconRotate>(zoom, feature);
- const float radialTextOffset = layout->evaluate<TextRadialOffset>(zoom, feature) * util::ONE_EM;
+ const float iconRotation = layout->evaluate<IconRotate>(zoom, feature);
+ const float textRotation = layout->evaluate<TextRotate>(zoom, feature);
+ std::array<float, 2> variableTextOffset;
+ if (!textRadialOffset.isUndefined()) {
+ variableTextOffset = {{layout->evaluate<TextRadialOffset>(zoom, feature) * util::ONE_EM,
+ INVALID_OFFSET_VALUE}};
+ } else {
+ variableTextOffset = {{layout->evaluate<TextOffset>(zoom, feature)[0] * util::ONE_EM,
+ layout->evaluate<TextOffset>(zoom, feature)[1] * util::ONE_EM}};
+ }
+
const SymbolPlacementType textPlacement = layout->get<TextRotationAlignment>() != AlignmentType::Map
? SymbolPlacementType::Point
: layout->get<SymbolPlacement>();
@@ -432,6 +525,22 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float textRepeatDistance = symbolSpacing / 2;
const auto evaluatedLayoutProperties = layout->evaluate(zoom, feature);
IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size());
+ const bool hasIconTextFit = evaluatedLayoutProperties.get<style::IconTextFit>() != IconTextFitType::None;
+
+ // Adjust shaped icon size when icon-text-fit is used.
+ optional<PositionedIcon> verticallyShapedIcon;
+ if (shapedIcon && hasIconTextFit) {
+ // Create vertically shaped icon for vertical writing mode if needed.
+ if (allowVerticalPlacement && shapedTextOrientations.vertical) {
+ verticallyShapedIcon = shapedIcon;
+ verticallyShapedIcon->fitIconToText(evaluatedLayoutProperties,
+ shapedTextOrientations.vertical,
+ layoutTextSize);
+ }
+ shapedIcon->fitIconToText(evaluatedLayoutProperties,
+ getDefaultHorizontalShaping(shapedTextOrientations),
+ layoutTextSize);
+ }
auto addSymbolInstance = [&] (Anchor& anchor, std::shared_ptr<SymbolInstanceSharedData> sharedData) {
assert(sharedData);
@@ -443,19 +552,20 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
// In tiled rendering mode, add all symbols in the buffers so that we can:
// (1) render symbols that overlap into this tile
// (2) approximate collision detection effects from neighboring symbols
- symbolInstances.emplace_back(anchor, std::move(sharedData), shapedTextOrientations, shapedIcon,
+ symbolInstances.emplace_back(anchor, std::move(sharedData), shapedTextOrientations,
+ shapedIcon, verticallyShapedIcon,
textBoxScale, textPadding, textPlacement, textOffset,
iconBoxScale, iconPadding, iconOffset, indexedFeature,
layoutFeatureIndex, feature.index,
feature.formattedText ? feature.formattedText->rawText() : std::u16string(),
- overscaling, rotation, radialTextOffset);
+ overscaling, iconRotation, textRotation, variableTextOffset, allowVerticalPlacement, iconType);
}
};
const auto createSymbolInstanceSharedData = [&] (GeometryCoordinates line) {
return std::make_shared<SymbolInstanceSharedData>(std::move(line),
- shapedTextOrientations, shapedIcon, evaluatedLayoutProperties, layoutTextSize,
- textPlacement, textOffset, glyphPositions);
+ shapedTextOrientations, shapedIcon, verticallyShapedIcon, evaluatedLayoutProperties,
+ textPlacement, textOffset, glyphPositions, allowVerticalPlacement);
};
const auto& type = feature.getType();
@@ -568,17 +678,45 @@ std::vector<float> CalculateTileDistances(const GeometryCoordinates& line, const
}
void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIndex>&, std::unordered_map<std::string, LayerRenderData>& renderData, const bool firstLoad, const bool showCollisionBoxes) {
- auto bucket = std::make_shared<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances), tilePixelRatio);
+ auto bucket = std::make_shared<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, iconsNeedLinear,
+ sortFeaturesByY, bucketLeaderID, std::move(symbolInstances), tilePixelRatio,
+ allowVerticalPlacement,
+ std::move(placementModes));
for (SymbolInstance &symbolInstance : bucket->symbolInstances) {
- const bool hasText = symbolInstance.hasText;
- const bool hasIcon = symbolInstance.hasIcon;
+ const bool hasText = symbolInstance.hasText();
+ const bool hasIcon = symbolInstance.hasIcon();
const bool singleLine = symbolInstance.singleLine;
const auto& feature = features.at(symbolInstance.layoutFeatureIndex);
// Insert final placement into collision tree and add glyphs/icons to buffers
+ // Process icon first, so that text symbols would have reference to iconIndex which
+ // is used when dynamic vertices for icon-text-fit image have to be updated.
+ if (hasIcon) {
+ const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
+ auto& iconBuffer = symbolInstance.hasSdfIcon() ? bucket->sdfIcon : bucket->icon;
+ const auto placeIcon = [&] (const SymbolQuad& iconQuad, auto& index, const WritingModeType writingMode) {
+ iconBuffer.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.iconOffset, writingMode, symbolInstance.line(), std::vector<float>());
+ index = iconBuffer.placedSymbols.size() - 1;
+ PlacedSymbol& iconSymbol = iconBuffer.placedSymbols.back();
+ iconSymbol.angle = (allowVerticalPlacement && writingMode == WritingModeType::Vertical) ? M_PI_2 : 0;
+ iconSymbol.vertexStartIndex = addSymbol(iconBuffer, sizeData, iconQuad,
+ symbolInstance.anchor, iconSymbol, feature.sortKey);
+ };
+
+ placeIcon(*symbolInstance.iconQuad(), symbolInstance.placedIconIndex, WritingModeType::None);
+ if (symbolInstance.verticalIconQuad()) {
+ placeIcon(*symbolInstance.verticalIconQuad(), symbolInstance.placedVerticalIconIndex, WritingModeType::Vertical);
+ }
+
+ for (auto& pair : bucket->paintProperties) {
+ pair.second.iconBinders.populateVertexVectors(feature, iconBuffer.vertices.elements(), {}, {});
+ }
+ }
+
if (hasText && feature.formattedText) {
optional<std::size_t> lastAddedSection;
if (singleLine) {
@@ -605,21 +743,6 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
updatePaintPropertiesForSection(*bucket, feature, *lastAddedSection);
}
- if (hasIcon) {
- if (symbolInstance.hasIcon) {
- const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
- bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
- symbolInstance.iconOffset, WritingModeType::None, symbolInstance.line(), std::vector<float>());
- symbolInstance.placedIconIndex = bucket->icon.placedSymbols.size() - 1;
- PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back();
- iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, *symbolInstance.iconQuad(),
- symbolInstance.anchor, iconSymbol, feature.sortKey);
-
- for (auto& pair : bucket->paintProperties) {
- pair.second.iconBinders.populateVertexVectors(feature, bucket->icon.vertices.elements(), {}, {});
- }
- }
- }
symbolInstance.releaseSharedData();
}
@@ -655,11 +778,12 @@ std::size_t SymbolLayout::addSymbolGlyphQuads(SymbolBucket& bucket,
optional<std::size_t> lastAddedSection) {
const Range<float> sizeData = bucket.textSizeBinder->getVertexSizeData(feature);
const bool hasFormatSectionOverrides = bucket.hasFormatSectionOverrides();
-
+ const auto& placedIconIndex = writingMode == WritingModeType::Vertical ? symbolInstance.placedVerticalIconIndex : symbolInstance.placedIconIndex;
bucket.text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
- symbolInstance.textOffset, writingMode, symbolInstance.line(), CalculateTileDistances(symbolInstance.line(), symbolInstance.anchor));
+ symbolInstance.textOffset, writingMode, symbolInstance.line(), CalculateTileDistances(symbolInstance.line(), symbolInstance.anchor), placedIconIndex);
placedIndex = bucket.text.placedSymbols.size() - 1;
PlacedSymbol& placedSymbol = bucket.text.placedSymbols.back();
+ placedSymbol.angle = (allowVerticalPlacement && writingMode == WritingModeType::Vertical) ? M_PI_2 : 0;
bool firstSymbol = true;
for (const auto& symbolQuad : glyphQuads) {
@@ -743,12 +867,15 @@ void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) {
return;
}
- for (const SymbolInstance &symbolInstance : symbolInstances) {
- auto populateCollisionBox = [&](const auto& feature) {
- SymbolBucket::CollisionBuffer& collisionBuffer = feature.alongLine ?
- static_cast<SymbolBucket::CollisionBuffer&>(bucket.getOrCreateCollisionCircleBuffer()) :
- static_cast<SymbolBucket::CollisionBuffer&>(bucket.getOrCreateCollisionBox());
- for (const CollisionBox &box : feature.boxes) {
+ for (const SymbolInstance& symbolInstance : symbolInstances) {
+ auto populateCollisionBox = [&](const auto& feature, bool isText) {
+ SymbolBucket::CollisionBuffer& collisionBuffer =
+ feature.alongLine ? (isText ? static_cast<SymbolBucket::CollisionBuffer&>(bucket.getOrCreateTextCollisionCircleBuffer())
+ : static_cast<SymbolBucket::CollisionBuffer&>(bucket.getOrCreateIconCollisionCircleBuffer()))
+ : (isText ? static_cast<SymbolBucket::CollisionBuffer&>(bucket.getOrCreateTextCollisionBox())
+ : static_cast<SymbolBucket::CollisionBuffer&>(bucket.getOrCreateIconCollisionBox()));
+
+ for (const CollisionBox& box : feature.boxes) {
auto& anchor = box.anchor;
Point<float> tl{box.x1, box.y1};
@@ -760,8 +887,11 @@ void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) {
const std::size_t indexLength = feature.alongLine ? 6 : 8;
if (collisionBuffer.segments.empty() || collisionBuffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
- collisionBuffer.segments.emplace_back(collisionBuffer.vertices.elements(),
- feature.alongLine ? bucket.collisionCircle->triangles.elements() : bucket.collisionBox->lines.elements());
+ collisionBuffer.segments.emplace_back(
+ collisionBuffer.vertices.elements(),
+ feature.alongLine
+ ? (isText ? bucket.textCollisionCircle->triangles.elements() : bucket.iconCollisionCircle->triangles.elements())
+ : (isText ? bucket.textCollisionBox->lines.elements() : bucket.iconCollisionBox->lines.elements()));
}
auto& segment = collisionBuffer.segments.back();
@@ -781,21 +911,29 @@ void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) {
collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
if (feature.alongLine) {
- bucket.collisionCircle->triangles.emplace_back(index, index + 1, index + 2);
- bucket.collisionCircle->triangles.emplace_back(index, index + 2, index + 3);
+ auto& collisionCircle = (isText ? bucket.textCollisionCircle : bucket.iconCollisionCircle);
+ collisionCircle->triangles.emplace_back(index, index + 1, index + 2);
+ collisionCircle->triangles.emplace_back(index, index + 2, index + 3);
} else {
- bucket.collisionBox->lines.emplace_back(index + 0, index + 1);
- bucket.collisionBox->lines.emplace_back(index + 1, index + 2);
- bucket.collisionBox->lines.emplace_back(index + 2, index + 3);
- bucket.collisionBox->lines.emplace_back(index + 3, index + 0);
+ auto& collisionBox = (isText ? bucket.textCollisionBox : bucket.iconCollisionBox);
+ collisionBox->lines.emplace_back(index + 0, index + 1);
+ collisionBox->lines.emplace_back(index + 1, index + 2);
+ collisionBox->lines.emplace_back(index + 2, index + 3);
+ collisionBox->lines.emplace_back(index + 3, index + 0);
}
segment.vertexLength += vertexLength;
segment.indexLength += indexLength;
}
};
- populateCollisionBox(symbolInstance.textCollisionFeature);
- populateCollisionBox(symbolInstance.iconCollisionFeature);
+ populateCollisionBox(symbolInstance.textCollisionFeature, true /*isText*/);
+ if (symbolInstance.verticalTextCollisionFeature) {
+ populateCollisionBox(*symbolInstance.verticalTextCollisionFeature, true /*isText*/);
+ }
+ if (symbolInstance.verticalIconCollisionFeature) {
+ populateCollisionBox(*symbolInstance.verticalIconCollisionFeature, false /*isText*/);
+ }
+ populateCollisionBox(symbolInstance.iconCollisionFeature, false /*isText*/);
}
}
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 581c3ccb04..1abcaaa5d6 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -45,15 +45,25 @@ public:
const std::string bucketLeaderID;
std::vector<SymbolInstance> symbolInstances;
- static Point<float> evaluateRadialOffset(style::SymbolAnchorType anchor, float radialOffset);
+ static constexpr float INVALID_OFFSET_VALUE = std::numeric_limits<float>::max();
+ /**
+ * @brief Calculates variable text offset.
+ *
+ * @param anchor text anchor
+ * @param textOffset Either `text-offset` or [ `text-radial-offset`, INVALID_OFFSET_VALUE ]
+ * @return std::array<float, 2> offset along x- and y- axis correspondingly.
+ */
+ static std::array<float, 2> evaluateVariableOffset(style::SymbolAnchorType anchor, std::array<float, 2> textOffset);
+
private:
void addFeature(const size_t,
const SymbolFeature&,
const ShapedTextOrientations& shapedTextOrientations,
- const optional<PositionedIcon>& shapedIcon,
+ optional<PositionedIcon> shapedIcon,
const GlyphPositions&,
- Point<float> textOffset);
+ std::array<float, 2> textOffset,
+ const SymbolContent iconType);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
@@ -93,12 +103,14 @@ private:
const uint32_t tileSize;
const float tilePixelRatio;
- bool sdfIcons = false;
bool iconsNeedLinear = false;
bool sortFeaturesByY = false;
+ bool allowVerticalPlacement = false;
+ std::vector<style::TextWritingModeType> placementModes;
style::TextSize::UnevaluatedType textSize;
style::IconSize::UnevaluatedType iconSize;
+ style::TextRadialOffset::UnevaluatedType textRadialOffset;
Immutable<style::SymbolLayoutProperties::PossiblyEvaluated> layout;
std::vector<SymbolFeature> features;
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
index aeea10ffa4..745c1c1d77 100644
--- a/src/mbgl/layout/symbol_projection.cpp
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -319,7 +319,11 @@ namespace mbgl {
const float glyphOffsetX = symbol.glyphOffsets[glyphIndex];
// Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed
auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, false);
- placedGlyphs.push_back(*placedGlyph);
+ if (placedGlyph) {
+ placedGlyphs.push_back(*placedGlyph);
+ } else {
+ placedGlyphs.emplace_back(Point<float>{-INFINITY, -INFINITY}, 0.0f, nullopt);
+ }
}
placedGlyphs.push_back(firstAndLastGlyph->second);
} else if (symbol.glyphOffsets.size() == 1) {
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index c35f33305c..649e0e321e 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -124,7 +124,7 @@ bool Map::isPanning() const {
#pragma mark -
-CameraOptions Map::getCameraOptions(const EdgeInsets& padding) const {
+CameraOptions Map::getCameraOptions(optional<EdgeInsets> padding) const {
return impl->transform.getCameraOptions(padding);
}
diff --git a/src/mbgl/map/map_impl.cpp b/src/mbgl/map/map_impl.cpp
index ce36583ab3..69c3de9783 100644
--- a/src/mbgl/map/map_impl.cpp
+++ b/src/mbgl/map/map_impl.cpp
@@ -130,11 +130,11 @@ void Map::Impl::onWillStartRenderingFrame() {
}
}
-void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepaint) {
+void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepaint, bool placemenChanged) {
rendererFullyLoaded = renderMode == RenderMode::Full;
if (mode == MapMode::Continuous) {
- observer.onDidFinishRenderingFrame(MapObserver::RenderMode(renderMode));
+ observer.onDidFinishRenderingFrame({MapObserver::RenderMode(renderMode), needsRepaint, placemenChanged});
if (needsRepaint || transform.inTransition()) {
onUpdate();
diff --git a/src/mbgl/map/map_impl.hpp b/src/mbgl/map/map_impl.hpp
index 13a68fb25e..416662f9e5 100644
--- a/src/mbgl/map/map_impl.hpp
+++ b/src/mbgl/map/map_impl.hpp
@@ -42,7 +42,7 @@ public:
void onInvalidate() final;
void onResourceError(std::exception_ptr) final;
void onWillStartRenderingFrame() final;
- void onDidFinishRenderingFrame(RenderMode, bool) final;
+ void onDidFinishRenderingFrame(RenderMode, bool, bool) final;
void onWillStartRenderingMap() final;
void onDidFinishRenderingMap() final;
void onStyleImageMissing(const std::string&, std::function<void()>) final;
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index e360150cc2..7ec41be37a 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -63,7 +63,7 @@ void Transform::resize(const Size size) {
#pragma mark - Camera
-CameraOptions Transform::getCameraOptions(const EdgeInsets& padding) const {
+CameraOptions Transform::getCameraOptions(optional<EdgeInsets> padding) const {
return state.getCameraOptions(padding);
}
@@ -82,6 +82,11 @@ void Transform::jumpTo(const CameraOptions& camera) {
* values for any options not included in `options`.
*/
void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& animation) {
+ Duration duration = animation.duration.value_or(Duration::zero());
+ if (state.bounds == LatLngBounds::unbounded() && !isGestureInProgress() && duration != Duration::zero()) {
+ // reuse flyTo, without exaggerated animation, to achieve constant ground speed.
+ return flyTo(camera, animation, true);
+ }
const EdgeInsets& padding = camera.padding.value_or(state.edgeInsets);
LatLng startLatLng = getLatLng(LatLng::Unwrapped);
const LatLng& unwrappedLatLng = camera.center.value_or(startLatLng);
@@ -91,6 +96,9 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
double pitch = camera.pitch ? *camera.pitch * util::DEG2RAD : getPitch();
if (std::isnan(zoom) || std::isnan(bearing) || std::isnan(pitch)) {
+ if (animation.transitionFinishFn) {
+ animation.transitionFinishFn();
+ }
return;
}
@@ -112,28 +120,25 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
// Constrain camera options.
zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom());
- const double scale = state.zoomScale(zoom);
pitch = util::clamp(pitch, util::PITCH_MIN, util::PITCH_MAX);
// Minimize rotation by taking the shorter path around the circle.
bearing = _normalizeAngle(bearing, state.bearing);
state.bearing = _normalizeAngle(state.bearing, bearing);
- Duration duration = animation.duration ? *animation.duration : Duration::zero();
-
- const double startScale = state.scale;
+ const double startZoom = state.getZoom();
const double startBearing = state.bearing;
const double startPitch = state.pitch;
state.panning = unwrappedLatLng != startLatLng;
- state.scaling = scale != startScale;
+ state.scaling = zoom != startZoom;
state.rotating = bearing != startBearing;
const EdgeInsets startEdgeInsets = state.edgeInsets;
startTransition(camera, animation, [=](double t) {
Point<double> framePoint = util::interpolate(startPoint, endPoint, t);
- LatLng frameLatLng = Projection::unproject(framePoint, startScale);
- double frameScale = util::interpolate(startScale, scale, t);
- state.setLatLngZoom(frameLatLng, state.scaleZoom(frameScale));
+ LatLng frameLatLng = Projection::unproject(framePoint, state.zoomScale(startZoom));
+ double frameZoom = util::interpolate(startZoom, zoom, t);
+ state.setLatLngZoom(frameLatLng, frameZoom);
if (bearing != startBearing) {
state.bearing = util::wrap(util::interpolate(startBearing, bearing, t), -M_PI, M_PI);
@@ -162,7 +167,7 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
Where applicable, local variable documentation begins with the associated
variable or function in van Wijk (2003). */
-void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &animation) {
+void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &animation, bool linearZoomInterpolation) {
const EdgeInsets& padding = camera.padding.value_or(state.edgeInsets);
const LatLng& latLng = camera.center.value_or(getLatLng(LatLng::Unwrapped)).wrapped();
double zoom = camera.zoom.value_or(getZoom());
@@ -170,6 +175,9 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
double pitch = camera.pitch ? *camera.pitch * util::DEG2RAD : getPitch();
if (std::isnan(zoom) || std::isnan(bearing) || std::isnan(pitch) || state.size.isEmpty()) {
+ if (animation.transitionFinishFn) {
+ animation.transitionFinishFn();
+ }
return;
}
@@ -209,16 +217,16 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
1.42 is the average value selected by participants in the user study in
van Wijk (2003). A value of 6<sup>¼</sup> would be equivalent to the
- root mean squared average velocity, V<sub>RMS</sub>. A value of 1 would
- produce a circular motion. */
+ root mean squared average velocity, V<sub>RMS</sub>. A value of 1
+ produces a circular motion. */
double rho = 1.42;
- if (animation.minZoom) {
- double minZoom = util::min(*animation.minZoom, startZoom, zoom);
+ if (animation.minZoom || linearZoomInterpolation) {
+ double minZoom = util::min(animation.minZoom.value_or(startZoom), startZoom, zoom);
minZoom = util::clamp(minZoom, state.getMinZoom(), state.getMaxZoom());
/// w<sub>m</sub>: Maximum visible span, measured in pixels with respect
/// to the initial scale.
double wMax = w0 / state.zoomScale(minZoom - startZoom);
- rho = std::sqrt(wMax / u1 * 2);
+ rho = u1 != 0 ? std::sqrt(wMax / u1 * 2) : 1.0;
}
/// ρ²
double rho2 = rho * rho;
@@ -233,8 +241,8 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
};
/// r₀: Zoom-out factor during ascent.
- double r0 = r(0);
- double r1 = r(1);
+ double r0 = u1 != 0 ? r(0) : INFINITY; // Silence division by 0 on sanitize bot.
+ double r1 = u1 != 0 ? r(1) : INFINITY;
// When u₀ = u₁, the optimal path doesn’t require both ascent and descent.
bool isClose = std::abs(u1) < 0.000001 || !std::isfinite(r0) || !std::isfinite(r1);
@@ -272,6 +280,9 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
if (duration == Duration::zero()) {
// Perform an instantaneous transition.
jumpTo(camera);
+ if (animation.transitionFinishFn) {
+ animation.transitionFinishFn();
+ }
return;
}
@@ -289,7 +300,8 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
// Calculate the current point and zoom level along the flight path.
Point<double> framePoint = util::interpolate(startPoint, endPoint, us);
- double frameZoom = startZoom + state.scaleZoom(1 / w(s));
+ double frameZoom = linearZoomInterpolation ? util::interpolate(startZoom, zoom, k)
+ : startZoom + state.scaleZoom(1 / w(s));
// Zoom can be NaN if size is empty.
if (std::isnan(frameZoom)) {
@@ -306,10 +318,10 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
if (padding != startEdgeInsets) {
// Interpolate edge insets
state.edgeInsets = {
- util::interpolate(startEdgeInsets.top(), padding.top(), us),
- util::interpolate(startEdgeInsets.left(), padding.left(), us),
- util::interpolate(startEdgeInsets.bottom(), padding.bottom(), us),
- util::interpolate(startEdgeInsets.right(), padding.right(), us)
+ util::interpolate(startEdgeInsets.top(), padding.top(), k),
+ util::interpolate(startEdgeInsets.left(), padding.left(), k),
+ util::interpolate(startEdgeInsets.bottom(), padding.bottom(), k),
+ util::interpolate(startEdgeInsets.right(), padding.right(), k)
};
}
auto maxPitch = getMaxPitchForEdgeInsets(state.edgeInsets);
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index 75dfeff645..30ce8a37a4 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -29,17 +29,21 @@ public:
// Camera
/** Returns the current camera options. */
- CameraOptions getCameraOptions(const EdgeInsets&) const;
+ CameraOptions getCameraOptions(optional<EdgeInsets>) const;
/** Instantaneously, synchronously applies the given camera options. */
void jumpTo(const CameraOptions&);
/** Asynchronously transitions all specified camera options linearly along
- an optional time curve. */
+ an optional time curve. However, center coordinate is not transitioned
+ linearly as, instead, ground speed is kept linear.*/
void easeTo(const CameraOptions&, const AnimationOptions& = {});
/** Asynchronously zooms out, pans, and zooms back into the given camera
along a great circle, as though the viewer is riding a supersonic
- jetcopter. */
- void flyTo(const CameraOptions&, const AnimationOptions& = {});
+ jetcopter.
+ Parameter linearZoomInterpolation: when true, there is no additional
+ zooming out as zoom is linearly interpolated from current to given
+ camera zoom. This is used for easeTo.*/
+ void flyTo(const CameraOptions&, const AnimationOptions& = {}, bool linearZoomInterpolation = false);
// Position
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index 77309a2a55..61007422cb 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -3,6 +3,7 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/interpolate.hpp>
#include <mbgl/util/projection.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/math/log2.hpp>
#include <mbgl/math/clamp.hpp>
@@ -145,10 +146,10 @@ ViewportMode TransformState::getViewportMode() const {
#pragma mark - Camera options
-CameraOptions TransformState::getCameraOptions(const EdgeInsets& padding) const {
+CameraOptions TransformState::getCameraOptions(optional<EdgeInsets> padding) const {
return CameraOptions()
.withCenter(getLatLng())
- .withPadding(padding)
+ .withPadding(padding ? padding : edgeInsets)
.withZoom(getZoom())
.withBearing(-bearing * util::RAD2DEG)
.withPitch(pitch * util::RAD2DEG);
@@ -289,9 +290,9 @@ ScreenCoordinate TransformState::latLngToScreenCoordinate(const LatLng& latLng)
return { p[0] / p[3], size.height - p[1] / p[3] };
}
-LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, LatLng::WrapMode wrapMode) const {
+TileCoordinate TransformState::screenCoordinateToTileCoordinate(const ScreenCoordinate& point, uint8_t atZoom) const {
if (size.isEmpty()) {
- return {};
+ return { {}, 0 };
}
float targetZ = 0;
@@ -325,7 +326,13 @@ LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, L
double z1 = coord1[2] / w1;
double t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0);
- return Projection::unproject(util::interpolate(p0, p1, t), scale / util::tileSize, wrapMode);
+ Point<double> p = util::interpolate(p0, p1, t) / scale * static_cast<double>(1 << atZoom);
+ return { { p.x, p.y }, static_cast<double>(atZoom) };
+}
+
+LatLng TransformState::screenCoordinateToLatLng(const ScreenCoordinate& point, LatLng::WrapMode wrapMode) const {
+ auto coord = screenCoordinateToTileCoordinate(point, 0);
+ return Projection::unproject(coord.p, 1 / util::tileSize, wrapMode);
}
mat4 TransformState::coordinatePointMatrix() const {
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index cca42db20f..10a92187d5 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -17,6 +17,7 @@
namespace mbgl {
class UnwrappedTileID;
+class TileCoordinate;
class TransformState {
friend class Transform;
@@ -42,7 +43,7 @@ public:
// Viewport mode
ViewportMode getViewportMode() const;
- CameraOptions getCameraOptions(const EdgeInsets&) const;
+ CameraOptions getCameraOptions(optional<EdgeInsets>) const;
// Position
LatLng getLatLng(LatLng::WrapMode = LatLng::Unwrapped) const;
@@ -81,6 +82,8 @@ public:
// Conversion
ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const;
LatLng screenCoordinateToLatLng(const ScreenCoordinate&, LatLng::WrapMode = LatLng::Unwrapped) const;
+ // Implements mapbox-gl-js pointCoordinate() : MercatorCoordinate.
+ TileCoordinate screenCoordinateToTileCoordinate(const ScreenCoordinate&, uint8_t atZoom) const;
double zoomScale(double zoom) const;
double scaleZoom(double scale) const;
diff --git a/src/mbgl/programs/gl/hillshade.cpp b/src/mbgl/programs/gl/hillshade.cpp
index b0c2c95aa8..18dd5a4caa 100644
--- a/src/mbgl/programs/gl/hillshade.cpp
+++ b/src/mbgl/programs/gl/hillshade.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<HillshadeProgram> {
static constexpr const char* name = "hillshade";
static constexpr const uint8_t hash[8] = { 0x8a, 0x11, 0x29, 0x18, 0x52, 0x7f, 0x3b, 0xbb };
- static constexpr const auto vertexOffset = 29113;
- static constexpr const auto fragmentOffset = 29284;
+ static constexpr const auto vertexOffset = 29125;
+ static constexpr const auto fragmentOffset = 29296;
};
constexpr const char* ShaderSource<HillshadeProgram>::name;
diff --git a/src/mbgl/programs/gl/hillshade_prepare.cpp b/src/mbgl/programs/gl/hillshade_prepare.cpp
index 1aef64293b..81f0296763 100644
--- a/src/mbgl/programs/gl/hillshade_prepare.cpp
+++ b/src/mbgl/programs/gl/hillshade_prepare.cpp
@@ -15,7 +15,7 @@ struct ShaderSource;
template <>
struct ShaderSource<HillshadePrepareProgram> {
static constexpr const char* name = "hillshade_prepare";
- static constexpr const uint8_t hash[8] = { 0xe6, 0x01, 0xf2, 0xbb, 0xa0, 0x77, 0x1d, 0xeb };
+ static constexpr const uint8_t hash[8] = { 0xbd, 0xa0, 0x8a, 0x88, 0x91, 0xe3, 0x73, 0x66 };
static constexpr const auto vertexOffset = 27698;
static constexpr const auto fragmentOffset = 27991;
};
@@ -68,11 +68,13 @@ varying vec2 v_pos;
uniform vec2 u_dimension;
uniform float u_zoom;
uniform float u_maxzoom;
+uniform vec4 u_unpack;
float getElevation(vec2 coord, float bias) {
// Convert encoded elevation value to meters
vec4 data = texture2D(u_image, coord) * 255.0;
- return (data.r + data.g * 256.0 + data.b * 256.0 * 256.0) / 4.0;
+ data.a = -1.0;
+ return dot(data, u_unpack) / 4.0;
}
void main() {
diff --git a/src/mbgl/programs/gl/line.cpp b/src/mbgl/programs/gl/line.cpp
index 8a04f86a5f..6bc7eb1fb0 100644
--- a/src/mbgl/programs/gl/line.cpp
+++ b/src/mbgl/programs/gl/line.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<LineProgram> {
static constexpr const char* name = "line";
static constexpr const uint8_t hash[8] = { 0x7f, 0x8e, 0xaa, 0x53, 0x75, 0x78, 0xac, 0x2c };
- static constexpr const auto vertexOffset = 30358;
- static constexpr const auto fragmentOffset = 33355;
+ static constexpr const auto vertexOffset = 30370;
+ static constexpr const auto fragmentOffset = 33367;
};
constexpr const char* ShaderSource<LineProgram>::name;
diff --git a/src/mbgl/programs/gl/line_gradient.cpp b/src/mbgl/programs/gl/line_gradient.cpp
index 189af6dbbc..776258bcdd 100644
--- a/src/mbgl/programs/gl/line_gradient.cpp
+++ b/src/mbgl/programs/gl/line_gradient.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<LineGradientProgram> {
static constexpr const char* name = "line_gradient";
static constexpr const uint8_t hash[8] = { 0x3f, 0xba, 0xc6, 0x33, 0xcd, 0x86, 0xa2, 0xe8 };
- static constexpr const auto vertexOffset = 34224;
- static constexpr const auto fragmentOffset = 37016;
+ static constexpr const auto vertexOffset = 34236;
+ static constexpr const auto fragmentOffset = 37028;
};
constexpr const char* ShaderSource<LineGradientProgram>::name;
diff --git a/src/mbgl/programs/gl/line_pattern.cpp b/src/mbgl/programs/gl/line_pattern.cpp
index 96da8a4f2a..df34ac572b 100644
--- a/src/mbgl/programs/gl/line_pattern.cpp
+++ b/src/mbgl/programs/gl/line_pattern.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<LinePatternProgram> {
static constexpr const char* name = "line_pattern";
static constexpr const uint8_t hash[8] = { 0x38, 0x9c, 0x3d, 0xde, 0xb4, 0xe0, 0xd1, 0x61 };
- static constexpr const auto vertexOffset = 37846;
- static constexpr const auto fragmentOffset = 41240;
+ static constexpr const auto vertexOffset = 37858;
+ static constexpr const auto fragmentOffset = 41252;
};
constexpr const char* ShaderSource<LinePatternProgram>::name;
diff --git a/src/mbgl/programs/gl/line_sdf.cpp b/src/mbgl/programs/gl/line_sdf.cpp
index 493cc76d01..cbf659ef3c 100644
--- a/src/mbgl/programs/gl/line_sdf.cpp
+++ b/src/mbgl/programs/gl/line_sdf.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<LineSDFProgram> {
static constexpr const char* name = "line_sdf";
static constexpr const uint8_t hash[8] = { 0x25, 0x94, 0x7f, 0xad, 0x84, 0xfe, 0x96, 0xad };
- static constexpr const auto vertexOffset = 43595;
- static constexpr const auto fragmentOffset = 47282;
+ static constexpr const auto vertexOffset = 43607;
+ static constexpr const auto fragmentOffset = 47294;
};
constexpr const char* ShaderSource<LineSDFProgram>::name;
diff --git a/src/mbgl/programs/gl/raster.cpp b/src/mbgl/programs/gl/raster.cpp
index ce46b1f299..5743348c9f 100644
--- a/src/mbgl/programs/gl/raster.cpp
+++ b/src/mbgl/programs/gl/raster.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<RasterProgram> {
static constexpr const char* name = "raster";
static constexpr const uint8_t hash[8] = { 0x40, 0x3d, 0x6c, 0xf4, 0xd0, 0x41, 0x51, 0x0e };
- static constexpr const auto vertexOffset = 48827;
- static constexpr const auto fragmentOffset = 49176;
+ static constexpr const auto vertexOffset = 48839;
+ static constexpr const auto fragmentOffset = 49188;
};
constexpr const char* ShaderSource<RasterProgram>::name;
diff --git a/src/mbgl/programs/gl/shader_source.cpp b/src/mbgl/programs/gl/shader_source.cpp
index 6e61578b5e..5d0e377d96 100644
--- a/src/mbgl/programs/gl/shader_source.cpp
+++ b/src/mbgl/programs/gl/shader_source.cpp
@@ -12,453 +12,454 @@ namespace gl {
constexpr const uint8_t compressedShaderSource[] = {
0x78, 0xda, 0xed, 0x3d, 0xfd, 0x6f, 0xe3, 0x36, 0xb2, 0xf7, 0x73, 0xfe, 0x0a, 0x17, 0x05, 0x0e,
0x92, 0x2c, 0x5b, 0xb6, 0x93, 0xec, 0x47, 0x75, 0x7a, 0xc5, 0xa2, 0xbb, 0xed, 0x0b, 0xd0, 0xee,
- 0x2e, 0x36, 0xed, 0xbd, 0xc3, 0x15, 0x0b, 0x43, 0xb2, 0x65, 0x47, 0xef, 0x6c, 0xcb, 0x4f, 0x56,
- 0x12, 0x3b, 0x87, 0xfc, 0xef, 0x8f, 0x33, 0xfc, 0x10, 0x49, 0x51, 0xf2, 0x47, 0x62, 0x27, 0x9b,
- 0x33, 0x8a, 0x6e, 0x2c, 0x72, 0x38, 0x1c, 0x92, 0xc3, 0xe1, 0xcc, 0x90, 0x1c, 0x7e, 0x9f, 0x8c,
- 0x86, 0xf1, 0xa8, 0xf1, 0xcb, 0xaf, 0xfd, 0x0f, 0x97, 0x27, 0xf3, 0x2c, 0x1e, 0x24, 0x8b, 0x24,
- 0x9d, 0x35, 0xae, 0x92, 0xf1, 0xd5, 0xbc, 0x31, 0x9a, 0xa4, 0x61, 0xee, 0x9f, 0x7c, 0x1f, 0x4f,
- 0x16, 0xf1, 0xc9, 0xf7, 0xc9, 0xa8, 0xf1, 0x1d, 0x81, 0x4d, 0x66, 0xf1, 0xd0, 0x9a, 0xa4, 0xb7,
- 0x73, 0xfb, 0xe4, 0x7b, 0xfa, 0xd9, 0x80, 0x2f, 0x02, 0x35, 0x1b, 0x26, 0x23, 0x15, 0x6c, 0x1a,
- 0x0f, 0x93, 0xeb, 0xa9, 0x04, 0xc9, 0x12, 0x8c, 0xc0, 0x58, 0x67, 0x01, 0x8a, 0x9f, 0x02, 0x90,
- 0xfe, 0xb9, 0x89, 0x07, 0xbd, 0xc6, 0xf5, 0x6c, 0x1e, 0x0e, 0xfe, 0xd5, 0x47, 0xe2, 0xac, 0x41,
- 0x3a, 0x5b, 0xe4, 0x94, 0xd0, 0x06, 0x24, 0xc7, 0xc3, 0xbf, 0x87, 0x93, 0xeb, 0xd8, 0x6e, 0xfc,
- 0x3b, 0x99, 0xf1, 0x94, 0x8b, 0x59, 0x8e, 0x89, 0x01, 0x49, 0xb2, 0x64, 0x20, 0x1f, 0x60, 0x6e,
- 0x3a, 0x81, 0x0a, 0xe6, 0xf5, 0xce, 0x5f, 0xf9, 0x59, 0x9c, 0x5f, 0x67, 0xb3, 0x06, 0x54, 0x68,
- 0xdd, 0x74, 0x5c, 0x15, 0xa2, 0x75, 0xd3, 0x71, 0x08, 0x90, 0xed, 0xdf, 0xcb, 0x04, 0xa5, 0xe4,
- 0xdf, 0x24, 0x5f, 0x19, 0x48, 0xfa, 0x44, 0x73, 0x18, 0x51, 0xe4, 0x7f, 0x96, 0x20, 0x11, 0xc4,
- 0x41, 0xbc, 0x9e, 0x52, 0x35, 0x6d, 0x64, 0x51, 0xc2, 0xf6, 0xba, 0xbd, 0xd7, 0xed, 0x8e, 0x3b,
- 0x4d, 0x87, 0x6a, 0x41, 0xb7, 0xd7, 0xee, 0xd8, 0x94, 0xa0, 0xb3, 0xc6, 0x30, 0x1e, 0xa4, 0xc3,
- 0xb8, 0x3f, 0x48, 0x27, 0x69, 0xc6, 0xc8, 0x41, 0x42, 0xe3, 0x19, 0xa4, 0x0f, 0x7f, 0x82, 0x74,
- 0x42, 0x4c, 0x51, 0xd1, 0x99, 0xa5, 0x74, 0xaa, 0x0c, 0xf7, 0x67, 0xe7, 0x2b, 0x21, 0xea, 0xfc,
- 0x9c, 0x54, 0x5a, 0x0d, 0xd3, 0xe5, 0x30, 0x27, 0x84, 0x04, 0xda, 0x72, 0x06, 0x3c, 0x4d, 0x96,
- 0x7d, 0x6c, 0x89, 0x44, 0x86, 0x34, 0x04, 0xae, 0xdc, 0x59, 0x79, 0x41, 0x13, 0x29, 0x26, 0x8f,
- 0x14, 0x21, 0xc2, 0x95, 0x3f, 0xbb, 0x5f, 0xdd, 0x9c, 0x37, 0x56, 0xaa, 0x48, 0x6b, 0xf0, 0x19,
- 0xab, 0x09, 0x69, 0x5c, 0x94, 0xaa, 0x42, 0x88, 0x69, 0x32, 0xc3, 0xec, 0x40, 0xe9, 0x33, 0xa4,
- 0x58, 0x2e, 0x5c, 0x10, 0xc0, 0xbe, 0x49, 0x8b, 0x6d, 0x9f, 0x62, 0x08, 0x97, 0x1b, 0x61, 0xe8,
- 0x69, 0x18, 0x4e, 0x01, 0x83, 0xd4, 0x5c, 0x4e, 0x89, 0xcb, 0x11, 0xf2, 0x26, 0xf6, 0x1a, 0xe3,
- 0x38, 0xef, 0xcf, 0xc3, 0x3c, 0x8f, 0xb3, 0x59, 0x7f, 0x9e, 0x2e, 0x94, 0xbe, 0x4c, 0x96, 0xf1,
- 0x84, 0xd4, 0x99, 0x66, 0xc3, 0xfe, 0xf5, 0x7c, 0x1e, 0x67, 0x6e, 0x45, 0x26, 0x99, 0xa3, 0x5a,
- 0x26, 0x43, 0xb8, 0x48, 0xee, 0xb4, 0x61, 0x48, 0x26, 0x71, 0xff, 0x7a, 0x96, 0xe4, 0x8b, 0x7e,
- 0x9e, 0xf6, 0x11, 0xc7, 0x42, 0x29, 0x98, 0x2e, 0x68, 0xef, 0xf5, 0x1a, 0xe9, 0x68, 0xb4, 0x88,
- 0xf3, 0x00, 0xb8, 0x91, 0xff, 0x5f, 0x26, 0x48, 0xae, 0xc8, 0x86, 0x79, 0xd3, 0xee, 0x98, 0xd2,
- 0x9a, 0x65, 0x6a, 0x15, 0x28, 0xde, 0x57, 0x96, 0x89, 0x3e, 0x87, 0x10, 0xd5, 0xa4, 0xd4, 0xd8,
- 0x9e, 0x5c, 0xcc, 0xbf, 0x3f, 0xf9, 0xcb, 0xf7, 0x66, 0x19, 0xc7, 0x64, 0xd1, 0xf3, 0x93, 0x72,
- 0x7f, 0x21, 0xf4, 0x67, 0x49, 0x74, 0x9d, 0xc7, 0xb4, 0xc3, 0x43, 0x18, 0x74, 0x9f, 0xb4, 0x78,
- 0x94, 0x66, 0x53, 0xc2, 0x6f, 0x39, 0x61, 0xfa, 0x3e, 0xf9, 0x93, 0x25, 0x4b, 0xff, 0x26, 0x4d,
- 0x86, 0x24, 0x29, 0x99, 0x59, 0x64, 0x4c, 0xc6, 0x93, 0xfe, 0xe7, 0x74, 0x91, 0xe4, 0xa4, 0x75,
- 0x01, 0x87, 0x70, 0x70, 0x7a, 0x23, 0x0a, 0xb7, 0xe3, 0x76, 0x6d, 0xe8, 0x10, 0x8e, 0x8a, 0xce,
- 0x1f, 0xca, 0xb1, 0x02, 0x3f, 0x9b, 0xbe, 0x5c, 0x9c, 0x95, 0x6a, 0xf8, 0x39, 0x0b, 0xc7, 0x94,
- 0xe1, 0x59, 0x49, 0xa7, 0x80, 0x3d, 0x61, 0x5d, 0xfd, 0xe9, 0xef, 0x1f, 0xbe, 0xbc, 0xff, 0xf2,
- 0xee, 0x7f, 0xfa, 0x17, 0x1f, 0x2f, 0x3f, 0x7f, 0xf8, 0xe9, 0xf7, 0x4f, 0x5f, 0x4e, 0x94, 0x92,
- 0x48, 0x53, 0x97, 0x48, 0x2c, 0x9f, 0xb7, 0x59, 0xa2, 0x4a, 0x6d, 0xa0, 0x44, 0x2b, 0x91, 0xb4,
- 0x7d, 0x79, 0x6c, 0xfb, 0x61, 0x6d, 0x6e, 0x54, 0xca, 0xd5, 0x59, 0xb3, 0x0e, 0x00, 0x39, 0xb0,
- 0xd4, 0x29, 0x8b, 0x41, 0x38, 0x91, 0xeb, 0x55, 0xd3, 0xa3, 0x52, 0xba, 0x89, 0x57, 0x7d, 0xe3,
- 0xf0, 0xde, 0x84, 0xd9, 0x2a, 0x99, 0x8d, 0x69, 0xd2, 0x0d, 0x24, 0x91, 0x6a, 0x0c, 0x89, 0xd1,
- 0x0e, 0x43, 0xce, 0xd0, 0x05, 0xba, 0x1c, 0x31, 0x74, 0x89, 0x6b, 0xe8, 0x05, 0x57, 0xb4, 0xdb,
- 0xd1, 0x07, 0xc0, 0x35, 0x37, 0xd1, 0xc5, 0xca, 0x79, 0xc5, 0xd1, 0x83, 0x2b, 0x8e, 0xf4, 0x8a,
- 0xa3, 0x35, 0x15, 0xab, 0x4c, 0x2e, 0xb3, 0x46, 0x3e, 0xa9, 0x66, 0x9b, 0x28, 0xab, 0xce, 0x23,
- 0xe5, 0xa2, 0x9a, 0x72, 0xa5, 0xbc, 0x3c, 0x5e, 0xa2, 0xfc, 0xd1, 0x39, 0x62, 0x2a, 0xf1, 0xb4,
- 0x3e, 0xd5, 0x78, 0xfa, 0x22, 0x9c, 0xce, 0x27, 0x71, 0xd6, 0x7b, 0x4f, 0xf2, 0x92, 0x69, 0x38,
- 0x8e, 0x77, 0xe5, 0x0e, 0xcc, 0x41, 0x0c, 0xd8, 0xab, 0x28, 0xa8, 0x59, 0x69, 0x17, 0xa7, 0x1f,
- 0x17, 0xe8, 0x01, 0xac, 0x40, 0x6a, 0x27, 0x79, 0xa2, 0x0d, 0xae, 0xda, 0x43, 0x52, 0x46, 0x81,
- 0x9a, 0x2d, 0x86, 0x28, 0x10, 0xba, 0x01, 0xc9, 0x27, 0xb2, 0x3a, 0xee, 0xbd, 0xb7, 0x58, 0x03,
- 0x5c, 0xca, 0x0e, 0x2a, 0x39, 0x84, 0x31, 0x0a, 0x82, 0x22, 0x95, 0xa0, 0x5e, 0x99, 0xa2, 0xa8,
- 0x8a, 0xa2, 0xc8, 0x48, 0x51, 0x3f, 0x92, 0x69, 0xea, 0x99, 0x69, 0xea, 0xd9, 0xbe, 0x22, 0x98,
- 0xa0, 0x52, 0xda, 0x06, 0x97, 0x16, 0x73, 0x71, 0xc4, 0xec, 0xc3, 0xc8, 0xb8, 0x28, 0x4d, 0x27,
- 0x42, 0x98, 0xdc, 0x26, 0xf9, 0x15, 0x01, 0x98, 0xeb, 0xb9, 0xf3, 0x24, 0x1f, 0x5c, 0x95, 0x73,
- 0x19, 0xdb, 0x91, 0x46, 0x66, 0xd7, 0x44, 0x0b, 0x41, 0x1c, 0x22, 0x13, 0x96, 0x2e, 0xc1, 0x6d,
- 0xc3, 0xf8, 0x26, 0x19, 0xc4, 0x6c, 0xb6, 0x65, 0x21, 0x11, 0x1d, 0x02, 0x4e, 0x52, 0xfb, 0x61,
- 0x5d, 0x08, 0xa7, 0x71, 0x16, 0xc2, 0xe4, 0x1a, 0xc4, 0x33, 0xd2, 0xd9, 0xfd, 0x61, 0xb2, 0xc8,
- 0xc3, 0xd9, 0x20, 0x5e, 0x2b, 0xc1, 0x4e, 0x09, 0x3b, 0x0e, 0xc3, 0x3c, 0xc4, 0xce, 0x9a, 0x41,
- 0x6f, 0xfd, 0xf7, 0xbb, 0xcb, 0xfe, 0x1f, 0x1f, 0x2f, 0x7e, 0xfe, 0xf4, 0xe5, 0xb7, 0x3e, 0x5b,
- 0x37, 0x4e, 0x8c, 0xd4, 0x61, 0x56, 0x3f, 0x97, 0xaa, 0xa0, 0x44, 0xe1, 0x50, 0x86, 0x6c, 0xad,
- 0xe2, 0x55, 0x49, 0x59, 0x34, 0x83, 0xad, 0xe2, 0x6a, 0x7b, 0x94, 0x65, 0x4e, 0x5a, 0x94, 0x0d,
- 0x84, 0x65, 0x21, 0x59, 0xb9, 0x17, 0x66, 0xca, 0x68, 0x9e, 0x42, 0x1a, 0x57, 0x21, 0x58, 0x2f,
- 0x50, 0x08, 0x41, 0x9d, 0xa2, 0x60, 0x34, 0x58, 0xa6, 0x46, 0xa1, 0x0a, 0xc3, 0x2b, 0x59, 0x43,
- 0x65, 0x34, 0xb9, 0xae, 0xe8, 0x3d, 0xc8, 0x51, 0x28, 0xc4, 0x5c, 0x46, 0x1e, 0x64, 0x0a, 0xe2,
- 0xa4, 0x62, 0x98, 0xae, 0xd1, 0x55, 0xc2, 0xba, 0x86, 0x24, 0x36, 0x3b, 0xcc, 0xe5, 0x59, 0x66,
- 0x25, 0x61, 0x42, 0xd5, 0x28, 0xd3, 0x56, 0xcc, 0xba, 0x6a, 0xf2, 0x24, 0x98, 0x1a, 0x0a, 0x17,
- 0x79, 0x96, 0xfe, 0x2b, 0xae, 0x63, 0x3d, 0x19, 0xa2, 0x9a, 0x03, 0x65, 0x28, 0x13, 0x23, 0x2a,
- 0xf9, 0x75, 0xfc, 0xa8, 0x03, 0xae, 0xa7, 0xfd, 0x36, 0x19, 0xe6, 0x57, 0xb5, 0xb4, 0x23, 0x44,
- 0x1d, 0x8b, 0xca, 0x70, 0x15, 0x8c, 0xaa, 0x80, 0xac, 0x61, 0x57, 0x1d, 0x76, 0x7d, 0x1b, 0x6a,
- 0x19, 0x45, 0x85, 0xa9, 0xe4, 0x17, 0x15, 0xcc, 0xc4, 0x36, 0x1a, 0x44, 0x1d, 0xf7, 0x94, 0x41,
- 0xa9, 0xab, 0x41, 0xac, 0xa3, 0xf0, 0x8b, 0xac, 0xa5, 0xb5, 0xa2, 0x6c, 0x40, 0x15, 0x62, 0xdd,
- 0x14, 0x65, 0xf2, 0xca, 0x15, 0x62, 0xcd, 0xe6, 0xa4, 0xe8, 0x92, 0x2b, 0xd8, 0x4a, 0x3e, 0xd1,
- 0x3f, 0x81, 0x6e, 0x63, 0x73, 0x09, 0xe4, 0x16, 0xc2, 0x4a, 0x54, 0x68, 0x12, 0x46, 0xc1, 0x36,
- 0xf2, 0x06, 0xfe, 0x31, 0xd4, 0x08, 0xc9, 0x2e, 0x17, 0x3c, 0xa2, 0x36, 0x4d, 0xba, 0x04, 0x5b,
- 0xc9, 0x10, 0xf6, 0xd7, 0x50, 0x19, 0xcb, 0x71, 0x25, 0x91, 0x62, 0xaa, 0x52, 0x20, 0xd8, 0x45,
- 0x34, 0xc8, 0x1f, 0xa6, 0x11, 0x95, 0xf3, 0x5d, 0x5d, 0x68, 0x98, 0xc6, 0x57, 0x45, 0xb8, 0xf3,
- 0xb4, 0x97, 0x3f, 0x0c, 0x5d, 0x23, 0x67, 0xbb, 0xba, 0x3c, 0xa8, 0xe0, 0x02, 0x15, 0xe5, 0x03,
- 0x26, 0xb3, 0xfa, 0x59, 0x4d, 0x5d, 0x31, 0x7e, 0xfa, 0x4c, 0x37, 0x0d, 0xa3, 0x8e, 0xb5, 0x72,
- 0xaa, 0xa2, 0x6f, 0x8b, 0x2a, 0x3e, 0x01, 0x56, 0x08, 0x6a, 0x25, 0xb5, 0x7f, 0xc0, 0x2d, 0xe6,
- 0x90, 0x7f, 0x5a, 0x85, 0x76, 0x39, 0x48, 0xb2, 0x01, 0xd1, 0xaf, 0xa8, 0x4e, 0x13, 0x90, 0xaa,
- 0x70, 0x58, 0x09, 0xb0, 0xd3, 0x69, 0x9f, 0xdb, 0x3e, 0x31, 0xd6, 0x2d, 0x5d, 0xcb, 0xe2, 0xaa,
- 0xf4, 0x20, 0xcd, 0x66, 0x44, 0x0f, 0x9a, 0x73, 0x8b, 0x4b, 0x41, 0xc5, 0x4a, 0xaa, 0xda, 0x1b,
- 0x29, 0xa9, 0x15, 0x6a, 0x06, 0x8c, 0x54, 0xc7, 0xa2, 0x93, 0xaf, 0x29, 0xf7, 0x3b, 0x68, 0x9a,
- 0xaa, 0x0e, 0x77, 0xdf, 0x80, 0x7e, 0x61, 0xee, 0xaa, 0x79, 0x96, 0xfe, 0x6f, 0x3c, 0xc8, 0xe3,
- 0x21, 0x27, 0x5f, 0xb5, 0xf9, 0x14, 0x7a, 0xa8, 0xed, 0xf7, 0xb0, 0xda, 0x1d, 0x4b, 0xaf, 0xb1,
- 0x7d, 0xeb, 0xd5, 0x68, 0x86, 0xc4, 0xf2, 0xaa, 0xb6, 0x48, 0x35, 0x52, 0x98, 0x3b, 0x82, 0x35,
- 0xaf, 0xa6, 0x58, 0xb9, 0x51, 0x15, 0x3d, 0x2d, 0xe1, 0x68, 0x2f, 0x57, 0xdb, 0x36, 0xb5, 0x4e,
- 0xe1, 0x35, 0x50, 0xb9, 0x43, 0x0d, 0x72, 0xe9, 0x5b, 0xff, 0xfe, 0x5e, 0xe2, 0xf5, 0x70, 0x96,
- 0x27, 0xe1, 0x24, 0x09, 0x17, 0x28, 0x2e, 0x09, 0xb3, 0x7a, 0x26, 0x45, 0xdd, 0x33, 0xd6, 0xe3,
- 0x53, 0x65, 0x1b, 0x58, 0xff, 0xd4, 0x62, 0x35, 0xb6, 0x97, 0x2e, 0xff, 0xb5, 0x72, 0x15, 0xe4,
- 0x68, 0x1c, 0x6f, 0xad, 0xab, 0xef, 0x5b, 0xe3, 0x3e, 0x8c, 0xce, 0xbc, 0x67, 0xe5, 0xf7, 0x70,
- 0xfa, 0xeb, 0x53, 0xe8, 0x9d, 0x4f, 0xa9, 0x2d, 0xee, 0x59, 0xc5, 0x63, 0xea, 0x5d, 0x15, 0xf3,
- 0x6f, 0xa2, 0xac, 0x55, 0x72, 0xf6, 0xc6, 0x8a, 0x57, 0x05, 0xcf, 0xae, 0x57, 0xa5, 0xaa, 0x19,
- 0x72, 0x53, 0x9d, 0x68, 0x0d, 0xb7, 0x6d, 0xaf, 0xcc, 0xac, 0x61, 0xa5, 0x9d, 0xd4, 0x90, 0xb5,
- 0x4c, 0xf2, 0x60, 0xd5, 0x01, 0xe5, 0x20, 0x91, 0xeb, 0x3e, 0x45, 0xc2, 0x85, 0xf7, 0x24, 0x9e,
- 0x8d, 0x09, 0x65, 0xf4, 0x0f, 0x17, 0xb0, 0xb6, 0x5f, 0x29, 0xbd, 0x19, 0x9e, 0x3b, 0x5f, 0xcb,
- 0x24, 0x6b, 0x28, 0xe6, 0xb7, 0xa6, 0xe1, 0xd2, 0x42, 0xbd, 0x59, 0x13, 0xcc, 0xca, 0x48, 0xf5,
- 0xf3, 0x60, 0x31, 0x4d, 0xd3, 0xfc, 0x6a, 0x91, 0xc7, 0x73, 0xab, 0xd3, 0xee, 0xb8, 0x3a, 0x22,
- 0x57, 0x25, 0x90, 0xaa, 0x38, 0x14, 0x07, 0x53, 0x47, 0x03, 0xb9, 0x2f, 0x1b, 0x7f, 0x6b, 0x10,
- 0x2c, 0xdd, 0xc6, 0x8f, 0xf0, 0xa7, 0xf1, 0x43, 0x43, 0xc2, 0x5e, 0xc2, 0x0c, 0xd5, 0x69, 0xd8,
- 0x29, 0xc3, 0x9a, 0x57, 0x20, 0xcd, 0x8b, 0x26, 0x5a, 0xe0, 0x08, 0x7f, 0x9a, 0xc3, 0x55, 0x3f,
- 0x99, 0x63, 0x1c, 0x4d, 0x2d, 0x94, 0x94, 0xe8, 0x07, 0xf8, 0xd8, 0xf6, 0xbd, 0x7b, 0x52, 0xbd,
- 0x21, 0x52, 0x90, 0xb3, 0xd6, 0xd5, 0xc7, 0x45, 0x93, 0xd9, 0x5f, 0x57, 0xe5, 0x18, 0xe6, 0xe9,
- 0x09, 0xd1, 0x4e, 0x66, 0x0b, 0xc8, 0xd9, 0x64, 0x2f, 0x81, 0xd5, 0x51, 0xb5, 0xc0, 0xdf, 0xc6,
- 0x64, 0x7a, 0xe7, 0x66, 0xc9, 0x49, 0xf3, 0xcc, 0xce, 0x10, 0xa8, 0x8d, 0xe6, 0x6b, 0x6e, 0x10,
- 0x5a, 0x98, 0x65, 0x19, 0x57, 0x22, 0x15, 0xfd, 0x21, 0x7c, 0x72, 0xdb, 0xa9, 0x10, 0x74, 0xb7,
- 0x53, 0xa6, 0xf5, 0x9f, 0x1f, 0xbe, 0x7c, 0x42, 0xb5, 0x0c, 0xb7, 0xb9, 0xbd, 0xee, 0xab, 0x76,
- 0xc7, 0x17, 0x9b, 0x78, 0xbf, 0xbc, 0xfb, 0xe3, 0xf2, 0xb2, 0xff, 0xd3, 0xa7, 0x0f, 0x3f, 0x93,
- 0xa9, 0x75, 0xfa, 0xf6, 0xcd, 0xdb, 0xb3, 0x5e, 0xef, 0x4d, 0xe7, 0xac, 0xd3, 0x3d, 0x3b, 0xed,
- 0xbd, 0xde, 0xd8, 0x93, 0xc0, 0xc6, 0x81, 0xfe, 0x31, 0xd8, 0x50, 0x34, 0xc3, 0x2d, 0x06, 0x45,
- 0x33, 0x36, 0xe5, 0x6e, 0x0f, 0xb6, 0xeb, 0x5b, 0xf3, 0x1a, 0xf5, 0xe8, 0xde, 0x05, 0x76, 0x5a,
- 0x02, 0x79, 0x7d, 0xd8, 0xdf, 0xc8, 0x62, 0xa3, 0x08, 0x2f, 0x83, 0xc5, 0xff, 0x65, 0xb9, 0xd5,
- 0x22, 0xc9, 0xce, 0x24, 0x1d, 0x5b, 0x30, 0x1a, 0x1e, 0x6d, 0xa0, 0x27, 0xcd, 0x06, 0xaf, 0x18,
- 0x08, 0xdb, 0xf6, 0x4e, 0xc9, 0x10, 0x09, 0xe6, 0x0f, 0x2e, 0x1d, 0xbd, 0x62, 0x5f, 0x13, 0xfe,
- 0x5c, 0x97, 0xa7, 0x44, 0x97, 0xec, 0x30, 0x6a, 0x7f, 0xa5, 0x0b, 0x3a, 0xcb, 0x75, 0xb3, 0xb1,
- 0xc9, 0x80, 0xa9, 0x94, 0x30, 0x8a, 0x12, 0x98, 0x99, 0x92, 0x5c, 0x50, 0x67, 0x42, 0x31, 0xa3,
- 0x77, 0x9a, 0xbc, 0x8f, 0x35, 0x01, 0xb7, 0x64, 0xe8, 0x4a, 0xbd, 0x89, 0x91, 0xb5, 0x09, 0x63,
- 0xd2, 0xdc, 0x61, 0xd0, 0x22, 0xdd, 0xe8, 0x90, 0x31, 0xc3, 0xff, 0x87, 0x69, 0x6e, 0x89, 0xb6,
- 0xbb, 0xe2, 0x17, 0xe7, 0x87, 0x9b, 0x70, 0x12, 0x50, 0x34, 0x8e, 0xd4, 0x75, 0x4e, 0x41, 0xb6,
- 0x13, 0x2f, 0xe7, 0xd6, 0x50, 0x5b, 0x96, 0x70, 0xe0, 0x48, 0x51, 0xd8, 0x69, 0xe2, 0xff, 0xdb,
- 0x07, 0xd8, 0xb3, 0xbe, 0x4d, 0xb3, 0xc9, 0x70, 0xd3, 0x5d, 0xdf, 0xad, 0xd6, 0x24, 0x87, 0x21,
- 0x97, 0xb6, 0x79, 0xdb, 0xcb, 0x20, 0xa4, 0x7f, 0xd9, 0xf7, 0x0a, 0xc4, 0x56, 0x8b, 0xa6, 0xad,
- 0x64, 0x06, 0x2c, 0xef, 0x32, 0x9a, 0x72, 0xe8, 0x9a, 0x9c, 0x91, 0x94, 0xea, 0x13, 0x02, 0x6b,
- 0x1a, 0xc1, 0x8e, 0x95, 0x18, 0x76, 0xe0, 0x10, 0xd8, 0x6e, 0x67, 0xd2, 0x46, 0x9d, 0x02, 0x55,
- 0x54, 0xee, 0xa2, 0x94, 0xc8, 0x5d, 0x98, 0x6b, 0xda, 0xb0, 0x3e, 0xe8, 0x10, 0x42, 0x67, 0x13,
- 0xe5, 0xa1, 0x94, 0x18, 0xce, 0x06, 0x57, 0x69, 0x66, 0xce, 0xe3, 0x13, 0xb6, 0x8c, 0x69, 0x12,
- 0x0e, 0x62, 0x03, 0x1f, 0x2c, 0xae, 0x92, 0x51, 0xee, 0x6f, 0xc4, 0x49, 0xf5, 0xda, 0x42, 0xb5,
- 0xfb, 0x82, 0x8f, 0x10, 0x9b, 0x3d, 0x9c, 0x14, 0x3d, 0x79, 0x96, 0xe6, 0x7f, 0x2c, 0x20, 0x5d,
- 0xdb, 0x43, 0x96, 0xfc, 0x4e, 0x9f, 0x53, 0x32, 0xdd, 0x4a, 0xac, 0x58, 0xf4, 0x07, 0xe5, 0x44,
- 0x79, 0xde, 0x17, 0x54, 0x31, 0x28, 0x4e, 0x55, 0xa0, 0x22, 0x6d, 0xdf, 0xaa, 0xc5, 0xd2, 0xc9,
- 0x04, 0xcf, 0xe9, 0xf4, 0xe7, 0x71, 0xb6, 0x98, 0x13, 0xb8, 0xe4, 0x26, 0xa6, 0x5e, 0x90, 0x60,
- 0x30, 0x21, 0x1c, 0x41, 0x86, 0xee, 0xbc, 0x09, 0x32, 0xc3, 0xaa, 0x69, 0xb9, 0x57, 0x59, 0xbb,
- 0x8d, 0x3a, 0xee, 0x19, 0x0c, 0xff, 0x5a, 0xc5, 0x8f, 0x8b, 0x0a, 0xdd, 0xfb, 0x63, 0x89, 0xd1,
- 0x6e, 0xb2, 0x61, 0x5c, 0xe3, 0xf3, 0x71, 0x6a, 0x5a, 0xe5, 0xf3, 0x61, 0x09, 0x38, 0xab, 0xe0,
- 0x14, 0x66, 0x63, 0x52, 0x24, 0xae, 0x64, 0xff, 0xcd, 0x2e, 0x03, 0xca, 0xac, 0x92, 0xc9, 0xfc,
- 0x2a, 0x0c, 0x48, 0xff, 0xf9, 0x46, 0x29, 0x87, 0x8d, 0xe6, 0x0d, 0x77, 0x10, 0x18, 0xbd, 0x6e,
- 0xbc, 0xaa, 0xc6, 0x7f, 0x35, 0x60, 0x2a, 0x9a, 0x14, 0x60, 0xa9, 0x24, 0x4e, 0x57, 0x56, 0xfa,
- 0x9e, 0x16, 0x67, 0x24, 0x19, 0xcb, 0x3b, 0x41, 0xbb, 0xeb, 0xdf, 0x1f, 0x64, 0x16, 0x3e, 0xab,
- 0xe9, 0xa6, 0xa5, 0x6b, 0xdb, 0xc8, 0x9a, 0x22, 0x60, 0x4e, 0xe6, 0x4a, 0xca, 0x71, 0xe6, 0x1a,
- 0x67, 0xae, 0x4c, 0xdf, 0x3c, 0x1c, 0x0e, 0x49, 0x07, 0xf6, 0x47, 0xe1, 0x20, 0x4f, 0xc1, 0xd7,
- 0xda, 0x2b, 0x4d, 0x6c, 0xc1, 0x3f, 0xa5, 0xe9, 0xac, 0x16, 0xde, 0xc7, 0xec, 0xe6, 0xe3, 0x1f,
- 0x84, 0xd1, 0xa2, 0x10, 0x30, 0xed, 0x95, 0x2d, 0x29, 0xb3, 0x05, 0x7d, 0x2a, 0x3d, 0xbe, 0xc6,
- 0x0f, 0xc1, 0x16, 0x3e, 0xee, 0x5a, 0xea, 0x25, 0xb5, 0x41, 0xac, 0xfe, 0x37, 0x04, 0x0a, 0x3d,
- 0xf0, 0xbc, 0xee, 0xe7, 0xc1, 0xf5, 0xba, 0x78, 0x93, 0x54, 0x8b, 0xad, 0x85, 0xdb, 0x60, 0x57,
- 0xa9, 0x36, 0x60, 0xe2, 0xac, 0xc7, 0x8f, 0x43, 0x2b, 0x04, 0x6b, 0x1e, 0x25, 0xad, 0x39, 0x76,
- 0xad, 0xfb, 0xa9, 0xd0, 0x85, 0x1d, 0x13, 0x4e, 0xdf, 0xe0, 0x52, 0xeb, 0x12, 0x8b, 0xd5, 0x08,
- 0xec, 0x19, 0x46, 0x51, 0xb1, 0xe0, 0xf8, 0xa8, 0xd4, 0x55, 0xc5, 0xf9, 0x07, 0x58, 0x2a, 0x1e,
- 0x8e, 0x63, 0xe4, 0x5b, 0xa3, 0xef, 0xa8, 0xd6, 0xc1, 0xd5, 0x52, 0x36, 0x30, 0xa1, 0xa7, 0x5b,
- 0x3a, 0xe6, 0x4a, 0x2f, 0x13, 0x75, 0x3f, 0xde, 0x1f, 0xee, 0xe0, 0xac, 0x61, 0x97, 0x63, 0xed,
- 0x31, 0xd9, 0x2d, 0xe9, 0xfb, 0xd6, 0x4e, 0x4e, 0x3d, 0xa7, 0x03, 0x40, 0x25, 0x03, 0xf5, 0xc9,
- 0xcf, 0x6d, 0x1c, 0xe0, 0x68, 0xc3, 0xa6, 0x3c, 0xfc, 0x24, 0xbb, 0x7c, 0x8f, 0xbc, 0x43, 0xb6,
- 0xe7, 0x9d, 0x9b, 0xad, 0xb6, 0x4f, 0x0c, 0xb6, 0xe8, 0xa3, 0x1c, 0x15, 0xdd, 0x42, 0x56, 0x18,
- 0xfd, 0x0d, 0x06, 0xab, 0xbc, 0x6a, 0x70, 0xae, 0xf3, 0x49, 0x32, 0xab, 0x3d, 0x1a, 0xa7, 0x80,
- 0x54, 0xcb, 0x18, 0x05, 0xcc, 0x24, 0x6b, 0x54, 0x80, 0x3a, 0xae, 0x2a, 0x41, 0xbe, 0x20, 0xd9,
- 0xa3, 0x76, 0xb8, 0xf2, 0x65, 0x92, 0x45, 0x0a, 0x80, 0x5b, 0x1a, 0x0c, 0x93, 0x6c, 0xd2, 0x70,
- 0xee, 0xd4, 0x9b, 0x4f, 0x2d, 0xab, 0x90, 0x67, 0x03, 0x4b, 0xd5, 0xcd, 0x3d, 0x45, 0xe3, 0x6e,
- 0xc2, 0xa4, 0xf1, 0xc0, 0x2b, 0xcc, 0xb9, 0x5e, 0x3d, 0xdb, 0xb0, 0x15, 0xe3, 0x1f, 0x9c, 0x5d,
- 0x0f, 0x27, 0x0c, 0xd5, 0x86, 0xee, 0xc2, 0x27, 0x0f, 0x14, 0x92, 0x85, 0xaa, 0x58, 0xe8, 0xb1,
- 0x64, 0x60, 0x5a, 0x42, 0x0c, 0xa6, 0x19, 0x31, 0x88, 0x56, 0x5c, 0x43, 0xa4, 0xda, 0x3b, 0xf8,
- 0x2d, 0xb5, 0x2d, 0x50, 0xd0, 0xbe, 0x01, 0x8b, 0xae, 0x0a, 0xca, 0x64, 0x3b, 0x16, 0x16, 0xe7,
- 0x52, 0xf8, 0x70, 0x1e, 0xde, 0xc7, 0xbb, 0x6e, 0xc4, 0x4f, 0x6a, 0xa0, 0x75, 0xf3, 0xd0, 0xeb,
- 0x42, 0x9b, 0x4f, 0x87, 0x67, 0x7f, 0x86, 0x9b, 0x5f, 0xfa, 0x18, 0x65, 0xe9, 0xd4, 0x8c, 0x47,
- 0x86, 0x30, 0xd2, 0x0a, 0xcb, 0x94, 0x0c, 0xa4, 0x12, 0xcc, 0x6e, 0x8d, 0x4a, 0xd9, 0x26, 0xa2,
- 0xd9, 0xf0, 0xe8, 0x70, 0x1b, 0x10, 0x9e, 0xa7, 0xf5, 0x64, 0x13, 0x4b, 0x67, 0x1d, 0xd1, 0x79,
- 0x5a, 0x43, 0x32, 0xc9, 0xdc, 0x80, 0x60, 0x0a, 0xb5, 0xf9, 0x72, 0xf5, 0x54, 0xe7, 0x72, 0x95,
- 0xe1, 0x96, 0x3f, 0x82, 0xb0, 0x6f, 0x1a, 0x24, 0x69, 0xeb, 0x57, 0x1d, 0xc5, 0x60, 0xd7, 0xc1,
- 0x2a, 0x7e, 0x06, 0x61, 0xbf, 0xdc, 0xc9, 0xc6, 0x0a, 0x09, 0xac, 0xb9, 0xab, 0xe5, 0x5b, 0xb7,
- 0x70, 0xc3, 0x2a, 0x90, 0x69, 0x82, 0x63, 0x27, 0x0a, 0x04, 0x5c, 0xb5, 0x52, 0x21, 0xee, 0x6e,
- 0x7d, 0x1d, 0x47, 0x14, 0x14, 0x15, 0x99, 0x30, 0x28, 0xf9, 0xa4, 0x3c, 0xf3, 0x80, 0x81, 0xe0,
- 0xf9, 0x82, 0x0e, 0x39, 0x26, 0x64, 0xda, 0x4b, 0xbf, 0xb8, 0xfc, 0xab, 0xe6, 0xf0, 0xd3, 0x30,
- 0x40, 0xc2, 0x25, 0x73, 0x2a, 0xd1, 0x1c, 0x7e, 0xc0, 0x25, 0x4f, 0xd5, 0xf4, 0x5b, 0x7f, 0xb3,
- 0xf5, 0x1d, 0x68, 0x25, 0xc2, 0x7c, 0x3e, 0x09, 0x57, 0xec, 0x3a, 0x21, 0xdd, 0x0b, 0xb6, 0xe4,
- 0x2e, 0x68, 0x2f, 0x5b, 0x72, 0x9f, 0xb5, 0x97, 0xb6, 0x57, 0x90, 0xef, 0xaa, 0xa0, 0x2b, 0x15,
- 0x74, 0x25, 0x83, 0x9a, 0xea, 0x8b, 0xca, 0xf5, 0x45, 0x6a, 0x7d, 0x51, 0x4d, 0x7d, 0x91, 0x5a,
- 0x5f, 0x54, 0xaa, 0xef, 0x81, 0xb7, 0x2e, 0x45, 0x87, 0x3b, 0x6a, 0x27, 0xb9, 0x62, 0x8c, 0x1e,
- 0xeb, 0x9a, 0x25, 0x1b, 0x40, 0xb5, 0x9e, 0xa8, 0xa2, 0x9e, 0x1d, 0x34, 0xb2, 0x35, 0x57, 0x23,
- 0xab, 0x37, 0x22, 0xb9, 0x78, 0x1c, 0x85, 0xc3, 0x78, 0xaf, 0x4b, 0xde, 0xe1, 0x56, 0xad, 0x43,
- 0x2f, 0x37, 0xfb, 0x5c, 0x2b, 0xf6, 0x71, 0xf0, 0x50, 0xe9, 0xac, 0x5d, 0x04, 0x7a, 0x6d, 0x6f,
- 0x7c, 0x73, 0x02, 0x7b, 0xbb, 0x1b, 0xbc, 0x15, 0xf7, 0x77, 0x9f, 0xe8, 0xf6, 0x6e, 0xc5, 0xdd,
- 0xdd, 0x47, 0xbb, 0xb9, 0xbb, 0x67, 0xa3, 0xc2, 0x74, 0x17, 0x18, 0x04, 0x11, 0x73, 0xfd, 0x3b,
- 0x07, 0x0c, 0x7a, 0xf0, 0x3c, 0x2c, 0x89, 0xa3, 0xd9, 0x70, 0x34, 0x1b, 0x8e, 0x66, 0xc3, 0xd1,
- 0x6c, 0xa8, 0x30, 0x1b, 0xfe, 0x99, 0xa6, 0xd3, 0x87, 0x9b, 0x0e, 0x2f, 0xdd, 0x2a, 0x38, 0x48,
- 0xc0, 0x96, 0x5a, 0xd3, 0x41, 0x8c, 0xd3, 0x01, 0xcc, 0x87, 0x52, 0x5d, 0xeb, 0xad, 0x01, 0x45,
- 0xe7, 0x7f, 0x8c, 0x88, 0x28, 0x47, 0xd5, 0xff, 0xa8, 0xfa, 0x1f, 0x55, 0xff, 0x97, 0xa5, 0xfa,
- 0x6f, 0xa8, 0xa8, 0x1f, 0x46, 0x45, 0x3f, 0x25, 0xa9, 0x13, 0x38, 0x8c, 0xae, 0x06, 0x52, 0xe3,
- 0x53, 0x4a, 0x64, 0xcb, 0x1b, 0xb8, 0x8a, 0x2c, 0xc1, 0xdc, 0xe2, 0x06, 0x80, 0x2e, 0x09, 0x6f,
- 0xe2, 0x2c, 0x4f, 0x88, 0x84, 0xed, 0x8f, 0xe1, 0x1c, 0x4d, 0x3c, 0xcb, 0xfd, 0x5a, 0x89, 0xb4,
- 0xfe, 0xf0, 0x24, 0xe8, 0x85, 0x33, 0x52, 0x9e, 0xa0, 0x8c, 0x95, 0x8d, 0xe2, 0x33, 0x22, 0x34,
- 0xf9, 0xd6, 0x8f, 0xf9, 0x16, 0x6d, 0x58, 0x79, 0x4d, 0x96, 0xe4, 0x54, 0xdf, 0x13, 0x82, 0xdc,
- 0xfa, 0x9b, 0x08, 0x1c, 0xa2, 0x46, 0xb4, 0x5d, 0xd5, 0x5c, 0x54, 0xba, 0x5a, 0x73, 0x51, 0xe9,
- 0x6a, 0x83, 0xcb, 0x10, 0x57, 0x9b, 0xdc, 0x98, 0x79, 0xe8, 0x01, 0x9c, 0xcd, 0x4f, 0x52, 0x6c,
- 0xa2, 0x63, 0xe3, 0x78, 0xc8, 0xad, 0x80, 0x04, 0x53, 0x94, 0x0e, 0x92, 0xec, 0xf2, 0x41, 0x32,
- 0xde, 0x1b, 0xa2, 0x05, 0xb7, 0x18, 0x06, 0xb9, 0xf0, 0x55, 0xd5, 0x9d, 0xa5, 0x2b, 0x7e, 0x67,
- 0xe9, 0xaa, 0xee, 0xce, 0x12, 0x2f, 0xbe, 0xcd, 0x08, 0x94, 0x4f, 0x76, 0x3c, 0xe2, 0x21, 0x1e,
- 0x9c, 0xb5, 0x74, 0x86, 0x04, 0xd2, 0x54, 0x21, 0x52, 0xfb, 0xce, 0xc7, 0x8e, 0x82, 0x1b, 0x9c,
- 0xe0, 0x3a, 0x80, 0x0f, 0xdb, 0x67, 0xf4, 0xf3, 0x44, 0xfa, 0xc9, 0xdd, 0x0e, 0x34, 0x3c, 0x26,
- 0xc5, 0xd1, 0x5e, 0xe2, 0xf5, 0xa6, 0xb5, 0x4a, 0x60, 0x8e, 0x07, 0x09, 0x3b, 0x8d, 0x1f, 0x59,
- 0xd7, 0x34, 0x7e, 0xc0, 0xf1, 0x01, 0xd5, 0x50, 0xba, 0xdf, 0x79, 0x83, 0x41, 0x6d, 0xf1, 0x67,
- 0x3b, 0x73, 0x3a, 0xed, 0x5e, 0xb7, 0xf7, 0xaa, 0x49, 0x3f, 0xc7, 0xe4, 0xf3, 0x75, 0xf7, 0xbc,
- 0xc7, 0x3e, 0x23, 0xf2, 0xd9, 0x79, 0xdd, 0xeb, 0xf9, 0x6c, 0x7a, 0xab, 0x47, 0x1a, 0xc5, 0x29,
- 0x59, 0xca, 0xa8, 0xd3, 0x08, 0x64, 0x0c, 0xca, 0x24, 0x01, 0x77, 0xea, 0x16, 0xff, 0x20, 0x28,
- 0xa2, 0x69, 0x06, 0x32, 0xb0, 0xf0, 0xc7, 0x64, 0x70, 0x74, 0x34, 0x9d, 0x91, 0xbe, 0xa3, 0xc7,
- 0x7d, 0xe1, 0x32, 0x0f, 0x6d, 0xbf, 0xd7, 0x7d, 0x75, 0xfa, 0xe6, 0x0c, 0x42, 0xc9, 0x0a, 0x89,
- 0x68, 0x17, 0xd5, 0xcb, 0x25, 0x41, 0xa6, 0x83, 0x24, 0x6e, 0xe9, 0xd2, 0xd1, 0x86, 0xf8, 0xa8,
- 0x34, 0xab, 0xe8, 0x84, 0x66, 0x19, 0x0a, 0x30, 0xba, 0x12, 0x46, 0x1a, 0x34, 0x82, 0x0d, 0xc3,
- 0xaa, 0xf1, 0x5d, 0x00, 0x37, 0x3f, 0x1a, 0xff, 0x96, 0x20, 0x9c, 0x80, 0xd7, 0x58, 0x12, 0xb8,
- 0x76, 0xd3, 0x32, 0xa4, 0x3a, 0xb4, 0x79, 0x56, 0xde, 0x44, 0x36, 0x70, 0xe6, 0xe9, 0xad, 0x45,
- 0xc7, 0xcb, 0xeb, 0x9e, 0x77, 0xd8, 0x39, 0x51, 0x17, 0x5a, 0x42, 0x06, 0x83, 0x7c, 0xbc, 0x7d,
- 0xe3, 0x9a, 0x5b, 0x04, 0xb4, 0x62, 0xf0, 0xde, 0x3e, 0x1b, 0xcc, 0x26, 0xeb, 0x39, 0x3e, 0xb6,
- 0x32, 0x95, 0xf2, 0x5a, 0xd3, 0xce, 0x18, 0x7a, 0xa8, 0xeb, 0x54, 0xc6, 0xce, 0xb2, 0x6d, 0x36,
- 0xb0, 0x0c, 0xf1, 0x58, 0x45, 0x3c, 0xae, 0x46, 0x3c, 0xae, 0x47, 0x3c, 0xd6, 0x10, 0x47, 0x2a,
- 0xe2, 0xa8, 0x1a, 0x71, 0x54, 0x8f, 0x38, 0x52, 0x11, 0x3b, 0x92, 0xee, 0xa8, 0x1e, 0xed, 0x28,
- 0x96, 0xaa, 0x9a, 0xfb, 0xb8, 0xd2, 0x62, 0xf6, 0x6c, 0x3d, 0x74, 0xda, 0x0a, 0xc6, 0x8e, 0xe1,
- 0x1a, 0xfd, 0x77, 0x0f, 0x54, 0x0c, 0xf6, 0xa8, 0xb2, 0x3c, 0x40, 0xe7, 0xd8, 0x7c, 0x2f, 0x07,
- 0x86, 0x9c, 0xd5, 0x3b, 0x7e, 0x04, 0x05, 0x45, 0x09, 0x8d, 0x07, 0xcb, 0x9e, 0x29, 0x3a, 0x88,
- 0x41, 0x6f, 0x29, 0x61, 0x7d, 0x5c, 0xb5, 0x45, 0x26, 0xeb, 0x4a, 0xbd, 0x5e, 0x2d, 0x15, 0x35,
- 0xeb, 0x33, 0x06, 0xdc, 0x47, 0xd7, 0xe8, 0x33, 0x74, 0x8d, 0x22, 0x97, 0x6e, 0xa5, 0xaa, 0x69,
- 0x3c, 0xb9, 0x9d, 0xa6, 0xb6, 0xbd, 0x76, 0x56, 0xe2, 0xb4, 0x60, 0x17, 0x7e, 0x3a, 0xfa, 0x5e,
- 0xbf, 0x9d, 0x23, 0x1b, 0x75, 0x2a, 0x2f, 0xbb, 0x8f, 0x32, 0x1c, 0xc7, 0xe2, 0x06, 0x98, 0x0c,
- 0xf2, 0xf2, 0x9d, 0xb6, 0x8f, 0xa1, 0xf3, 0xd3, 0x9c, 0xbb, 0xa0, 0x42, 0xb7, 0x5f, 0x6b, 0x13,
- 0xdc, 0x89, 0x33, 0x33, 0xe0, 0xf2, 0xe1, 0xb8, 0x03, 0xd8, 0xd0, 0x6c, 0xfc, 0xf5, 0xaf, 0x0d,
- 0xae, 0xd9, 0x06, 0xa0, 0xd8, 0x4a, 0x09, 0x77, 0x04, 0x82, 0x2a, 0xdd, 0x27, 0x3f, 0xd2, 0x75,
- 0xf9, 0xe4, 0x07, 0xfa, 0xb6, 0x84, 0x3c, 0x9e, 0xee, 0x9d, 0xa3, 0x69, 0x20, 0x07, 0x39, 0xbf,
- 0x72, 0x98, 0xd3, 0x2b, 0xac, 0x16, 0xae, 0x3a, 0x54, 0x18, 0x3f, 0x1b, 0x5a, 0x2f, 0xa7, 0x8f,
- 0x61, 0xbd, 0xc0, 0x05, 0xc8, 0x97, 0x62, 0xb2, 0xf0, 0x6e, 0x6d, 0x67, 0x63, 0x61, 0x05, 0x54,
- 0x69, 0xff, 0x88, 0x19, 0xc3, 0xd4, 0x41, 0x53, 0x5c, 0xf6, 0xeb, 0xd4, 0x2e, 0xd9, 0x01, 0x2c,
- 0x0f, 0x6b, 0x91, 0x86, 0x4e, 0x33, 0x07, 0x0e, 0xbd, 0x93, 0xb0, 0x8b, 0x2e, 0xba, 0x5f, 0xa5,
- 0xf2, 0x50, 0x9a, 0xe1, 0xcb, 0xde, 0xba, 0xc0, 0x81, 0x5a, 0xaf, 0x61, 0x55, 0x8e, 0xc2, 0x66,
- 0xda, 0xd2, 0x71, 0xc3, 0xe3, 0xb8, 0xe1, 0xb1, 0xf3, 0x86, 0x07, 0x7b, 0xac, 0x68, 0xc9, 0x9e,
- 0x0f, 0xaa, 0xde, 0xf3, 0x28, 0x6d, 0x8d, 0xb0, 0x12, 0x8e, 0x26, 0xaf, 0xf6, 0xeb, 0x0f, 0x19,
- 0x26, 0x53, 0x58, 0x29, 0xd2, 0x99, 0xbf, 0x59, 0x28, 0x07, 0xd6, 0xec, 0xc7, 0x88, 0x89, 0x23,
- 0xc7, 0x31, 0xa0, 0x11, 0x9e, 0xe6, 0x8b, 0x64, 0x42, 0x60, 0x59, 0x50, 0x55, 0x41, 0x19, 0xbb,
- 0x29, 0x8d, 0x7a, 0xb0, 0x25, 0x65, 0x10, 0xb5, 0x11, 0x14, 0x36, 0x4f, 0x49, 0xe2, 0x67, 0x5f,
- 0x15, 0x52, 0xbd, 0x37, 0xdd, 0xb7, 0x18, 0xae, 0x0a, 0x91, 0x34, 0x59, 0x45, 0x35, 0x4f, 0x0e,
- 0x69, 0xcf, 0xaa, 0x61, 0x97, 0x6e, 0xb3, 0x30, 0x55, 0x77, 0xb3, 0xbe, 0xe6, 0xdd, 0xa5, 0x44,
- 0x52, 0x94, 0xde, 0x1e, 0x09, 0x97, 0x98, 0x4e, 0xbf, 0x89, 0xbe, 0xf5, 0x61, 0x12, 0xdf, 0x80,
- 0x8a, 0x34, 0xb3, 0x58, 0x78, 0x63, 0xc2, 0x91, 0x2e, 0x93, 0x81, 0x49, 0xb8, 0xe0, 0x31, 0x24,
- 0x30, 0xba, 0x6c, 0x99, 0x33, 0xe9, 0x74, 0x72, 0x30, 0x22, 0x9a, 0x78, 0x9d, 0x09, 0x43, 0x1f,
- 0x66, 0x4d, 0xfc, 0x33, 0x66, 0x2f, 0x3b, 0xe1, 0x47, 0x44, 0x3f, 0xe8, 0xbf, 0xb6, 0x47, 0x14,
- 0x52, 0xa2, 0x3d, 0x94, 0x1e, 0x2b, 0xa9, 0x1f, 0x2c, 0x54, 0x46, 0x25, 0xa2, 0xa1, 0x4f, 0x9a,
- 0xa8, 0xcc, 0xb6, 0x58, 0x41, 0xa2, 0x6d, 0x8b, 0x9f, 0x2b, 0xd4, 0xce, 0xb8, 0x66, 0x17, 0x55,
- 0x96, 0xed, 0x54, 0x15, 0x19, 0x54, 0x16, 0x59, 0x5b, 0xdb, 0x70, 0x13, 0x4a, 0x3b, 0x4a, 0x91,
- 0xd8, 0x50, 0x44, 0xce, 0x1f, 0x6d, 0x40, 0x8d, 0x8a, 0x71, 0xbc, 0x09, 0x11, 0x66, 0xfa, 0xaf,
- 0x6a, 0x7a, 0xcb, 0x5c, 0x22, 0xd9, 0x80, 0x3c, 0x73, 0xc9, 0x78, 0x19, 0x8e, 0xc7, 0x31, 0x06,
- 0x9a, 0x80, 0x19, 0x0d, 0x2c, 0xda, 0xf8, 0x5b, 0xa3, 0x87, 0x36, 0x52, 0xa7, 0x7d, 0x46, 0x0c,
- 0x24, 0x91, 0x78, 0xd6, 0x3e, 0xc7, 0xc4, 0xd3, 0x73, 0x92, 0x4a, 0xfe, 0x30, 0x4b, 0x30, 0xce,
- 0x92, 0x1b, 0x66, 0x00, 0x0e, 0x9a, 0x23, 0xf2, 0x5f, 0x62, 0xb7, 0xac, 0xb0, 0x39, 0x24, 0xff,
- 0x8d, 0x6d, 0xd7, 0x1a, 0x37, 0xaf, 0xc8, 0x7f, 0x34, 0x2d, 0x22, 0xff, 0x0d, 0x6c, 0xdb, 0x6b,
- 0x80, 0xea, 0x4b, 0xea, 0x70, 0x2d, 0x8a, 0xbc, 0x25, 0xa6, 0x07, 0x04, 0x56, 0x28, 0x08, 0x6a,
- 0x76, 0xdf, 0xb6, 0x09, 0xcb, 0xf6, 0x5a, 0x14, 0x4c, 0x8f, 0x3d, 0x85, 0xea, 0x2e, 0x0a, 0x1f,
- 0x24, 0xa2, 0xbd, 0x84, 0xb3, 0xef, 0x10, 0xdc, 0xc4, 0xa5, 0xdf, 0x2b, 0xf1, 0xcd, 0x03, 0x8e,
- 0x15, 0x16, 0xc3, 0x1e, 0xc4, 0xf0, 0x13, 0x49, 0x5b, 0x2a, 0x1c, 0x4d, 0xb2, 0xb1, 0x3e, 0xe0,
- 0xd8, 0x7a, 0x09, 0x37, 0x21, 0x15, 0x86, 0x33, 0xe9, 0x4a, 0x00, 0x4f, 0x47, 0xed, 0x4a, 0xf7,
- 0x92, 0x5f, 0x85, 0xc3, 0xf4, 0x56, 0x4f, 0x05, 0xa1, 0x6b, 0x04, 0x0f, 0x07, 0x10, 0x02, 0xa5,
- 0x08, 0xe3, 0xf8, 0xf9, 0xa2, 0x71, 0xda, 0xee, 0x9e, 0x75, 0xcf, 0xdf, 0xf6, 0x5e, 0x9d, 0x9f,
- 0x9e, 0xbf, 0x79, 0xfb, 0xfa, 0xed, 0xe9, 0x89, 0x21, 0xa2, 0x0e, 0x58, 0xa0, 0x95, 0xc1, 0xcc,
- 0x64, 0x8e, 0xb4, 0xe8, 0x6b, 0x78, 0xc4, 0x32, 0x82, 0x80, 0x86, 0xb6, 0x1c, 0xd1, 0x10, 0x57,
- 0x8c, 0x9f, 0x69, 0x04, 0x9a, 0x01, 0x31, 0x76, 0xc1, 0x3e, 0x0b, 0x67, 0x0b, 0xcb, 0x2a, 0x9a,
- 0xfc, 0x67, 0xe7, 0x6b, 0x4b, 0xfa, 0xea, 0x7e, 0xb5, 0x1d, 0x34, 0xf6, 0x58, 0x58, 0x37, 0xbb,
- 0xa9, 0x66, 0x0a, 0xcc, 0x93, 0x74, 0x1e, 0x07, 0x44, 0xe2, 0xce, 0x08, 0x74, 0xef, 0xdc, 0x61,
- 0x47, 0xa3, 0x91, 0x22, 0xdb, 0x93, 0xea, 0x15, 0xe7, 0xa2, 0x31, 0xd6, 0x4b, 0xc0, 0xf8, 0x97,
- 0x9a, 0x99, 0x64, 0x92, 0x21, 0x06, 0xc6, 0xc4, 0x6e, 0x8b, 0xe5, 0xda, 0x64, 0xda, 0x7d, 0xbe,
- 0xc0, 0xfb, 0x1d, 0x3c, 0x4f, 0xb8, 0x33, 0xc0, 0x11, 0xf1, 0x83, 0xdc, 0x46, 0x61, 0x2e, 0x06,
- 0x6c, 0xbc, 0x84, 0xcb, 0x2a, 0xbc, 0x4b, 0xa6, 0xd7, 0x18, 0xec, 0x97, 0xa6, 0xaf, 0x9a, 0x9f,
- 0x2f, 0x7c, 0x49, 0xf7, 0xee, 0xb6, 0xdf, 0xbc, 0x3e, 0x6f, 0x15, 0x81, 0xfd, 0xba, 0xed, 0xd7,
- 0xe7, 0x2c, 0x9f, 0x4c, 0x52, 0xfa, 0x98, 0x27, 0x04, 0x10, 0x12, 0xa5, 0x68, 0x64, 0xc7, 0x4b,
- 0x6c, 0xba, 0x28, 0x86, 0x4d, 0x01, 0x79, 0x41, 0xc6, 0x81, 0x4c, 0x76, 0x74, 0xac, 0x62, 0xef,
- 0xd0, 0x91, 0xf0, 0x8a, 0x54, 0x8e, 0x94, 0x66, 0xd8, 0x0e, 0xff, 0x86, 0x28, 0xb9, 0x50, 0x80,
- 0x93, 0x8d, 0x0c, 0x83, 0xe3, 0x25, 0x55, 0xc8, 0xf7, 0x4d, 0x31, 0x93, 0xed, 0xaf, 0xe2, 0x40,
- 0xd1, 0x14, 0x08, 0x09, 0x46, 0x7f, 0x31, 0xe3, 0xbb, 0x68, 0x56, 0xaf, 0xec, 0x7f, 0x00, 0x16,
- 0xa6, 0x21, 0x54, 0x40, 0xe1, 0xb4, 0xe8, 0xd8, 0x34, 0x59, 0x7f, 0xd9, 0xde, 0xe7, 0x0b, 0x94,
- 0x25, 0x05, 0x37, 0xd1, 0xa0, 0xcc, 0x50, 0x88, 0xd5, 0x4c, 0x9f, 0xcd, 0xa2, 0x33, 0xc1, 0x95,
- 0x98, 0xdf, 0x45, 0x20, 0xa2, 0xaa, 0x10, 0x4e, 0x96, 0x89, 0xaf, 0x27, 0x4a, 0x91, 0x43, 0x72,
- 0x0b, 0x29, 0x2b, 0x4a, 0x15, 0xb7, 0x43, 0xbb, 0x29, 0x7d, 0x3e, 0x4c, 0xb8, 0x89, 0x29, 0x89,
- 0x94, 0x62, 0xd8, 0xe2, 0xf3, 0x37, 0xaf, 0x4f, 0x3b, 0xdd, 0x57, 0x27, 0x26, 0x09, 0xc7, 0x7c,
- 0x91, 0xe5, 0x0d, 0x27, 0x8c, 0x6b, 0x5f, 0xaf, 0xb0, 0x96, 0x23, 0xc1, 0xca, 0x0f, 0x62, 0x31,
- 0x69, 0xa3, 0xbf, 0xe0, 0xb7, 0xe9, 0xbb, 0x5a, 0x9a, 0x7c, 0x63, 0x64, 0x6a, 0xa9, 0x18, 0xf1,
- 0xa6, 0x57, 0x8a, 0x87, 0x34, 0x0e, 0xa7, 0xd3, 0x90, 0x07, 0x38, 0x32, 0x84, 0xf7, 0x04, 0x7d,
- 0x7e, 0x16, 0x2f, 0xd2, 0x51, 0x98, 0x7d, 0x73, 0xc1, 0x62, 0x8e, 0x0f, 0x58, 0xed, 0x70, 0x96,
- 0x74, 0x1c, 0xce, 0x6b, 0x1e, 0x80, 0xe2, 0xb9, 0x75, 0xb1, 0x90, 0x39, 0xcc, 0xba, 0x68, 0xc8,
- 0x32, 0x5c, 0x5d, 0x97, 0xe1, 0x7b, 0xab, 0x15, 0x4d, 0xc2, 0xbc, 0xea, 0x0e, 0xc3, 0xec, 0xfa,
- 0x4e, 0x11, 0x20, 0x35, 0x24, 0xd4, 0x74, 0xc8, 0xda, 0xde, 0xd8, 0xa8, 0x2b, 0xd4, 0x7e, 0x78,
- 0x7e, 0x81, 0x85, 0x5e, 0xfa, 0xd3, 0x4c, 0x82, 0xe9, 0xd5, 0x81, 0xe1, 0xc9, 0x06, 0x42, 0x78,
- 0x96, 0x2b, 0xcf, 0x89, 0x8a, 0x40, 0xd5, 0x05, 0x9a, 0x2d, 0x79, 0x5e, 0x6e, 0x11, 0x7d, 0x03,
- 0xd9, 0xd0, 0x23, 0x98, 0xe1, 0x16, 0x73, 0xc1, 0xd8, 0x1f, 0xac, 0xf4, 0x36, 0xec, 0xae, 0xb6,
- 0xa1, 0xaa, 0x1f, 0x78, 0x27, 0xd4, 0xf7, 0x00, 0x6f, 0xbe, 0xda, 0x76, 0x9a, 0xf7, 0xee, 0xe3,
- 0xef, 0x17, 0xef, 0x7e, 0xbd, 0x78, 0x77, 0x79, 0xf1, 0xf1, 0x97, 0xea, 0x27, 0x69, 0x40, 0xc9,
- 0x57, 0x83, 0x6e, 0x06, 0x21, 0x7f, 0x48, 0xa1, 0xd5, 0xed, 0xbd, 0x21, 0xd9, 0x4c, 0x7b, 0xea,
- 0x8b, 0x6d, 0x81, 0x80, 0x86, 0xfb, 0xa6, 0xcf, 0x24, 0x60, 0x28, 0x47, 0xd0, 0x66, 0x7c, 0x69,
- 0x49, 0x0b, 0x44, 0x84, 0x6d, 0x0a, 0x04, 0xfe, 0x02, 0xbb, 0xc9, 0xbe, 0x6e, 0x9d, 0x57, 0x67,
- 0x2c, 0x4a, 0x78, 0xe1, 0x13, 0x94, 0x22, 0x72, 0xb3, 0x85, 0x96, 0xbe, 0xe7, 0xa4, 0xcc, 0x7a,
- 0xb1, 0xc1, 0x59, 0x40, 0x61, 0x50, 0x71, 0xb0, 0x3c, 0xc4, 0x0e, 0x1a, 0xff, 0xc1, 0xa3, 0x90,
- 0xfb, 0x7c, 0xe5, 0x66, 0x39, 0xbe, 0x60, 0x1b, 0xfe, 0x03, 0x3b, 0x81, 0x19, 0xce, 0xe1, 0x64,
- 0x44, 0x33, 0x8b, 0x1c, 0x36, 0xc8, 0x80, 0xcb, 0x61, 0xe3, 0xcc, 0x35, 0x64, 0x48, 0xe7, 0x58,
- 0x9a, 0x16, 0xff, 0x25, 0x74, 0x6a, 0x79, 0x08, 0xd0, 0xe2, 0x15, 0x8a, 0x62, 0x7a, 0x9d, 0x2b,
- 0x65, 0x45, 0xbd, 0x4e, 0x19, 0x4b, 0x0f, 0xdf, 0x7b, 0x00, 0x25, 0xab, 0x69, 0x15, 0xf4, 0x31,
- 0x3d, 0x9f, 0x3e, 0x06, 0x21, 0x57, 0xa4, 0xf5, 0x19, 0x5e, 0xb0, 0xa4, 0xd5, 0x39, 0x45, 0xe0,
- 0x49, 0xaa, 0x98, 0x68, 0xe2, 0x12, 0x95, 0x72, 0x69, 0x9c, 0xb5, 0xfc, 0x9c, 0x86, 0xa2, 0x26,
- 0xca, 0xed, 0xb5, 0x56, 0x07, 0xed, 0x95, 0x5e, 0x40, 0xff, 0xea, 0xd5, 0x38, 0x62, 0x44, 0x88,
- 0xf2, 0x06, 0x51, 0xa0, 0x5b, 0xd7, 0xee, 0x35, 0x3c, 0x8f, 0xae, 0xbd, 0x89, 0xc5, 0xf9, 0x4f,
- 0xb5, 0x53, 0x81, 0x7e, 0x8f, 0x29, 0x76, 0x7c, 0xcb, 0xaf, 0xee, 0x08, 0x66, 0xf1, 0x94, 0x78,
- 0x4f, 0x29, 0x86, 0xfd, 0x57, 0xaa, 0xcc, 0x18, 0xac, 0x11, 0x1f, 0xa5, 0x22, 0x5d, 0x26, 0x47,
- 0xd2, 0xe4, 0x77, 0x54, 0xe9, 0x4d, 0xd3, 0xca, 0x52, 0xa6, 0x22, 0xa5, 0x4a, 0x4b, 0xd7, 0xee,
- 0x9d, 0x92, 0x8e, 0x0a, 0x46, 0xb9, 0xa4, 0x42, 0x06, 0xeb, 0x09, 0xf4, 0xd6, 0x50, 0xe3, 0x73,
- 0x55, 0x95, 0x7a, 0x5a, 0x28, 0x47, 0xb8, 0xc8, 0xc2, 0xca, 0xbd, 0x9e, 0x2d, 0x95, 0x62, 0x4d,
- 0xfd, 0x35, 0xab, 0xca, 0x26, 0xa5, 0xf8, 0x69, 0xde, 0xab, 0xfa, 0x86, 0xde, 0x91, 0xda, 0x73,
- 0x94, 0xbc, 0xc3, 0xbc, 0x4e, 0x64, 0xba, 0xe4, 0x4d, 0x99, 0xc3, 0x76, 0x38, 0xf3, 0xb4, 0x17,
- 0x7e, 0x41, 0x42, 0x2f, 0xc0, 0xb7, 0x75, 0x9a, 0x55, 0x0b, 0x16, 0x14, 0x93, 0xd9, 0x48, 0xbe,
- 0x1c, 0x4e, 0x6d, 0xe1, 0x29, 0xe9, 0x31, 0xa8, 0xb0, 0x65, 0x89, 0x0a, 0xf2, 0x16, 0xa2, 0xb6,
- 0xdd, 0xa2, 0x4a, 0x0c, 0x4f, 0x6a, 0x7b, 0x98, 0x5e, 0x61, 0x30, 0xef, 0x21, 0x1c, 0x95, 0xb0,
- 0x8c, 0x7f, 0x7b, 0xf7, 0x8f, 0xfe, 0xaf, 0x17, 0x1f, 0x3f, 0xf4, 0xdf, 0x5f, 0x5c, 0xfe, 0xfe,
- 0xee, 0xe3, 0x4f, 0x1f, 0x1a, 0xa7, 0xbd, 0xd7, 0xaf, 0x5e, 0xb7, 0x3b, 0xcf, 0xc6, 0x76, 0xde,
- 0xf4, 0xd1, 0xe9, 0x0a, 0x1b, 0x7b, 0xff, 0xb6, 0x33, 0x11, 0xab, 0xe3, 0x2c, 0x5e, 0x54, 0x06,
- 0x07, 0x39, 0x5a, 0xa9, 0x47, 0x2b, 0xf5, 0xa5, 0x5a, 0xa9, 0x47, 0xbb, 0xf1, 0x68, 0x37, 0xbe,
- 0x30, 0xbb, 0x91, 0x8b, 0xf3, 0x4d, 0x4d, 0x47, 0xaf, 0xb4, 0x84, 0x1e, 0x8d, 0xc9, 0xa3, 0x31,
- 0x79, 0x34, 0x26, 0xbf, 0x29, 0x63, 0x72, 0xe3, 0xbd, 0xe5, 0x9d, 0xcd, 0xcc, 0x07, 0xea, 0x8f,
- 0x2f, 0xc6, 0x54, 0x3c, 0x9a, 0x7b, 0x06, 0x73, 0xaf, 0xe2, 0xdd, 0x32, 0x76, 0x20, 0x00, 0x38,
- 0x5b, 0x65, 0x95, 0xea, 0xf7, 0xcb, 0xf6, 0x63, 0x25, 0x96, 0x6d, 0x40, 0x9e, 0xa3, 0x2c, 0x7c,
- 0xfd, 0xcb, 0x9f, 0xde, 0xfd, 0xfa, 0x01, 0x44, 0xfb, 0xa3, 0xdb, 0x88, 0x6b, 0x76, 0x50, 0x1f,
- 0x66, 0x42, 0x3e, 0xc4, 0x46, 0x2c, 0x76, 0x4f, 0x77, 0x70, 0x32, 0x1d, 0x4d, 0xc3, 0x1d, 0x4c,
- 0xc3, 0x67, 0x60, 0x89, 0x3d, 0x3b, 0xeb, 0xf4, 0xc0, 0xa6, 0xe1, 0xf1, 0x12, 0xe8, 0x33, 0xba,
- 0x04, 0xfa, 0xc2, 0x4d, 0xf0, 0xa7, 0x35, 0x79, 0x9f, 0x9b, 0x03, 0xe0, 0x50, 0x26, 0xf8, 0x7f,
- 0xc2, 0xb5, 0xdc, 0x27, 0x72, 0x33, 0x70, 0xb8, 0xed, 0x36, 0xa9, 0x0d, 0x8a, 0xd6, 0xd1, 0xcf,
- 0x70, 0xf4, 0x33, 0x1c, 0xfd, 0x0c, 0x87, 0xf2, 0x33, 0x14, 0xd3, 0x35, 0x94, 0x95, 0xfe, 0xc7,
- 0xf5, 0x3f, 0x6c, 0x73, 0x05, 0x55, 0x91, 0x75, 0x7a, 0x6c, 0x97, 0xb5, 0x8e, 0x8c, 0xc3, 0x19,
- 0x39, 0x2f, 0xe5, 0x06, 0xe8, 0x7f, 0x82, 0x77, 0xe6, 0x19, 0x5d, 0x25, 0x7d, 0x42, 0xef, 0xd1,
- 0x31, 0x56, 0xf2, 0xcb, 0x7a, 0x41, 0x45, 0xee, 0x6a, 0xb9, 0x7d, 0x6a, 0x9b, 0xdb, 0x4b, 0x47,
- 0xf4, 0x9a, 0xa7, 0x46, 0x1d, 0xd6, 0x00, 0x57, 0x26, 0x9c, 0x91, 0x01, 0x27, 0xa1, 0xda, 0x61,
- 0xfd, 0x5d, 0x87, 0x11, 0x28, 0xf6, 0xbf, 0x31, 0x97, 0x25, 0xc5, 0xb6, 0x24, 0x5d, 0x49, 0xaf,
- 0x34, 0x8b, 0x25, 0xc2, 0x53, 0x7b, 0xba, 0xbd, 0x54, 0xe1, 0xa3, 0x7a, 0xf8, 0x48, 0x85, 0x5f,
- 0xf5, 0xf1, 0xf5, 0xe5, 0xa6, 0xe8, 0x0b, 0xa2, 0x07, 0xb1, 0xcb, 0x72, 0x82, 0x3e, 0xa4, 0xc9,
- 0xd2, 0x6a, 0x5d, 0x35, 0x7b, 0xec, 0x05, 0x19, 0x5b, 0x27, 0x68, 0x55, 0x20, 0x8f, 0x76, 0x41,
- 0x1e, 0x55, 0x23, 0x8f, 0x0a, 0xd6, 0xc0, 0x98, 0x27, 0xdb, 0x5f, 0x30, 0x47, 0x1e, 0x22, 0xbd,
- 0xea, 0x92, 0x96, 0xdb, 0x12, 0xae, 0x68, 0x87, 0xeb, 0xe1, 0x0c, 0x57, 0x44, 0x70, 0x45, 0xb6,
- 0xe2, 0x64, 0x06, 0x5c, 0xc6, 0x2b, 0xe2, 0xa4, 0x52, 0xd7, 0x9c, 0x11, 0xd9, 0xe6, 0xab, 0xe1,
- 0xd4, 0xed, 0xfc, 0x88, 0x4f, 0x59, 0x3c, 0x23, 0xa7, 0xf3, 0xa3, 0x1e, 0x4c, 0x62, 0x83, 0x44,
- 0x9f, 0xbe, 0x0e, 0x4b, 0xaa, 0x1d, 0xe9, 0xf5, 0xfe, 0x4a, 0x4a, 0x37, 0x95, 0x8a, 0x2a, 0x4a,
- 0x45, 0xfb, 0x38, 0x04, 0xc5, 0x52, 0xa1, 0x86, 0xd0, 0x94, 0x18, 0xed, 0x7e, 0xaa, 0xf2, 0x78,
- 0x95, 0xe8, 0x78, 0x48, 0xeb, 0x78, 0x48, 0xab, 0xca, 0x13, 0xcf, 0x87, 0xcc, 0xe0, 0xa6, 0x7b,
- 0x0c, 0x2f, 0x3d, 0xfa, 0x8a, 0x6a, 0xa8, 0x2c, 0xf2, 0x2b, 0xfb, 0xaa, 0x00, 0x31, 0xf1, 0x97,
- 0x94, 0x5b, 0xd7, 0x9b, 0x2a, 0xd8, 0xf1, 0x5e, 0xd4, 0xf1, 0x7c, 0xdb, 0x33, 0x39, 0xdf, 0x76,
- 0x38, 0x77, 0xba, 0x34, 0x15, 0x8b, 0x9f, 0x86, 0x9a, 0x8b, 0x4c, 0x57, 0x9d, 0x9f, 0xa6, 0x26,
- 0xcb, 0x98, 0x4c, 0x93, 0xec, 0xe8, 0xf5, 0x3e, 0x7a, 0xbd, 0x8f, 0x5e, 0xef, 0xa3, 0xd7, 0x7b,
- 0x33, 0xaf, 0x37, 0xaa, 0xff, 0x01, 0x93, 0x43, 0x62, 0xee, 0x3a, 0xba, 0x39, 0xd3, 0x5e, 0x7a,
- 0x92, 0x90, 0x12, 0x83, 0x57, 0x02, 0x5b, 0x35, 0x85, 0xa5, 0x63, 0x33, 0xec, 0xd1, 0x5a, 0xec,
- 0xd1, 0x66, 0xd8, 0x23, 0x09, 0x3b, 0x84, 0x43, 0xdb, 0xf7, 0xc9, 0x40, 0xdd, 0x16, 0x5b, 0x0c,
- 0x47, 0xd8, 0xfd, 0xe5, 0x78, 0x59, 0xc4, 0xa4, 0x7c, 0x66, 0x16, 0xd8, 0xf1, 0x5e, 0x5b, 0xbd,
- 0x0e, 0x70, 0x38, 0x1d, 0x7c, 0xbf, 0x0a, 0xf4, 0xb7, 0x78, 0x55, 0xcf, 0x88, 0xa1, 0xe6, 0xdc,
- 0x43, 0xb9, 0xc7, 0x6b, 0x3b, 0x7c, 0x6b, 0x55, 0xe9, 0x1b, 0x73, 0xc9, 0x12, 0x31, 0x04, 0xb9,
- 0xfd, 0xd0, 0x18, 0x5b, 0x0a, 0x85, 0x89, 0xdd, 0x0e, 0x35, 0xe0, 0xa8, 0x12, 0x38, 0x2a, 0x01,
- 0xa3, 0xf7, 0x50, 0xd4, 0xe2, 0x0a, 0x14, 0x2e, 0x4a, 0x3a, 0xdb, 0xa7, 0xbe, 0xc0, 0x40, 0x79,
- 0x1d, 0xf7, 0xbc, 0x55, 0xc8, 0x47, 0x59, 0x96, 0xd3, 0x08, 0xc6, 0xa6, 0x1c, 0x86, 0x76, 0xff,
- 0x87, 0x5c, 0x37, 0x3a, 0x77, 0x9a, 0x4f, 0xc8, 0x5a, 0x93, 0xc9, 0x4f, 0x63, 0x08, 0xa9, 0x8f,
- 0x6b, 0x4f, 0x45, 0x66, 0x74, 0x3d, 0x1a, 0xc5, 0x59, 0xdd, 0xb3, 0xb9, 0x5b, 0x06, 0x55, 0xeb,
- 0x18, 0xd2, 0xba, 0xbb, 0x46, 0x5a, 0xeb, 0x04, 0x96, 0x65, 0x8e, 0x44, 0xd9, 0x02, 0x3d, 0xda,
- 0x53, 0xe9, 0x6f, 0xd8, 0x10, 0x33, 0x8a, 0x96, 0xec, 0x06, 0x34, 0x30, 0x1f, 0x3c, 0xcf, 0x2e,
- 0x77, 0x00, 0x04, 0x13, 0x2b, 0xba, 0x4a, 0xea, 0x5c, 0x79, 0x03, 0xbb, 0x5f, 0xee, 0x27, 0xfd,
- 0x69, 0x91, 0xd2, 0x62, 0xdb, 0xa9, 0xce, 0xea, 0x6e, 0xdc, 0x4d, 0xa5, 0xd1, 0xc9, 0x20, 0xa2,
- 0x15, 0x51, 0x3a, 0x16, 0x10, 0x02, 0xbc, 0x2e, 0x1b, 0xc4, 0x65, 0x79, 0xe4, 0x43, 0xd2, 0x6d,
- 0x18, 0xdc, 0x4f, 0x7f, 0x6d, 0xa5, 0x70, 0x69, 0xce, 0xf2, 0x2c, 0x5c, 0x98, 0x5e, 0x63, 0x81,
- 0xa7, 0x51, 0x16, 0xf3, 0x64, 0xd6, 0xbf, 0xc5, 0x00, 0xbf, 0x5a, 0xb4, 0xbc, 0x42, 0x30, 0x77,
- 0xca, 0x13, 0xb3, 0x43, 0x43, 0xc4, 0x75, 0xd6, 0x44, 0xa0, 0xed, 0x52, 0xb0, 0x2e, 0x0d, 0xff,
- 0x4d, 0x91, 0xb5, 0x43, 0x6a, 0x21, 0x90, 0x2a, 0x58, 0x42, 0x36, 0x8e, 0x82, 0xe2, 0xa7, 0xc7,
- 0xc1, 0xfc, 0x7b, 0x51, 0xa8, 0xab, 0x17, 0xea, 0x16, 0x85, 0xba, 0x45, 0xa1, 0x2e, 0x14, 0xd2,
- 0xb6, 0x18, 0x28, 0x36, 0x97, 0x05, 0x98, 0xe5, 0xa3, 0xcf, 0x5e, 0x7d, 0x6a, 0x87, 0x72, 0x18,
- 0x6e, 0xec, 0x12, 0x81, 0x17, 0xd0, 0xfa, 0xf0, 0x85, 0xe1, 0xbb, 0x21, 0x74, 0x3a, 0xf9, 0x70,
- 0xd5, 0x1e, 0x83, 0x27, 0x05, 0x6c, 0xb7, 0x22, 0xef, 0x6e, 0xb9, 0xaa, 0xcc, 0x5b, 0xdd, 0x2d,
- 0x45, 0x94, 0xbb, 0xf0, 0x26, 0x26, 0xd2, 0x21, 0x0e, 0xf8, 0x13, 0x49, 0xfc, 0xdd, 0x2b, 0xfe,
- 0xe0, 0x95, 0xed, 0x9d, 0x42, 0x50, 0x52, 0x08, 0x49, 0x6e, 0x31, 0xd8, 0x16, 0xf9, 0x82, 0x60,
- 0x6c, 0xe5, 0xe1, 0x07, 0x82, 0xa1, 0x42, 0x9c, 0x3d, 0x4e, 0x69, 0xf8, 0xe9, 0xf4, 0xa1, 0x43,
- 0x0f, 0x0c, 0x05, 0xd6, 0x3d, 0x6d, 0xa0, 0xce, 0x8a, 0xee, 0xda, 0x04, 0x9b, 0x23, 0x22, 0xbf,
- 0x2b, 0xf0, 0x40, 0x15, 0xee, 0x06, 0x29, 0x9a, 0x84, 0x45, 0x49, 0x41, 0xa3, 0xbf, 0x71, 0x22,
- 0x5d, 0x51, 0x8d, 0x8b, 0x6d, 0x67, 0xc3, 0xe7, 0xf2, 0x80, 0x6d, 0x0f, 0x93, 0xbf, 0xa4, 0x97,
- 0x16, 0x39, 0x9b, 0x32, 0x9f, 0x2f, 0x82, 0x52, 0xe8, 0xc4, 0xf2, 0xce, 0x0d, 0x58, 0xf2, 0xcc,
- 0xa8, 0x36, 0xef, 0xea, 0x28, 0xa9, 0xa7, 0x50, 0x42, 0x98, 0x53, 0xaa, 0xcc, 0xe5, 0x4e, 0x09,
- 0x64, 0x4c, 0x5d, 0x08, 0x45, 0x69, 0x3a, 0x01, 0x21, 0xb3, 0xa0, 0x7b, 0x7b, 0x10, 0xb6, 0xb3,
- 0x8f, 0xc4, 0x86, 0x92, 0xa4, 0xd7, 0x80, 0x46, 0x71, 0x88, 0xb2, 0xb4, 0x04, 0xa7, 0x3e, 0x11,
- 0x88, 0xb0, 0x75, 0x99, 0x15, 0x59, 0x83, 0x70, 0x4a, 0x78, 0x10, 0x4c, 0x3e, 0x08, 0xa1, 0x47,
- 0x24, 0x33, 0x7f, 0x78, 0xa1, 0x02, 0x7e, 0x9e, 0xe4, 0x83, 0x2b, 0x9d, 0xd6, 0x2c, 0xcd, 0xc3,
- 0x3c, 0xee, 0x2f, 0x56, 0xd3, 0x28, 0x9d, 0x54, 0x14, 0xa4, 0x01, 0x03, 0x35, 0x13, 0x48, 0x11,
- 0xe2, 0x83, 0x2b, 0x25, 0x9e, 0x66, 0xc5, 0x06, 0x1a, 0x4d, 0x9d, 0x84, 0x11, 0xd1, 0x80, 0xe6,
- 0x93, 0x70, 0x16, 0x57, 0x40, 0xd0, 0x20, 0xd6, 0x5a, 0x5e, 0xd1, 0xb7, 0x20, 0xdc, 0xf4, 0x64,
- 0x6c, 0x1b, 0x35, 0x59, 0xa7, 0xe1, 0xbc, 0xea, 0x04, 0x55, 0xd9, 0x6c, 0x2a, 0x19, 0x4d, 0xca,
- 0xe0, 0x7f, 0x03, 0x7b, 0x31, 0xcf, 0xe4, 0xd1, 0xf4, 0x42, 0x89, 0x09, 0xe4, 0x29, 0x29, 0xce,
- 0xb6, 0x70, 0x0f, 0xad, 0x9a, 0xcb, 0x4f, 0xc6, 0xa0, 0xda, 0x51, 0x38, 0x13, 0x79, 0x22, 0x8c,
- 0x19, 0x4f, 0x25, 0xa0, 0x32, 0x53, 0x2e, 0xe2, 0xf1, 0x14, 0xe2, 0x46, 0x12, 0xb6, 0x9b, 0xc4,
- 0x41, 0x4b, 0x9b, 0xd4, 0x7f, 0xf6, 0xbe, 0x72, 0x2d, 0x15, 0x86, 0x1d, 0x56, 0xaf, 0xef, 0x2a,
- 0xe6, 0x2e, 0xbc, 0x6a, 0xf2, 0x5d, 0xf5, 0x94, 0x25, 0x9d, 0x8a, 0x54, 0x80, 0x0c, 0xa4, 0x04,
- 0xfd, 0xd9, 0xf9, 0xea, 0xb2, 0x5f, 0xdd, 0xaf, 0x2e, 0x9f, 0xbd, 0xb6, 0x87, 0x21, 0xa8, 0xfd,
- 0xfb, 0x06, 0xf4, 0x59, 0x03, 0x6a, 0x7c, 0x58, 0x85, 0xa2, 0xb2, 0x32, 0xe2, 0xba, 0xa6, 0xac,
- 0x47, 0xcc, 0x44, 0x0a, 0xc3, 0xa7, 0xa6, 0xa9, 0xfe, 0xb4, 0xcf, 0x69, 0x32, 0xcb, 0xd7, 0x45,
- 0x43, 0x67, 0x41, 0xad, 0x85, 0x28, 0x22, 0xf2, 0xe7, 0x2a, 0x2d, 0x44, 0x51, 0xa0, 0x62, 0x6b,
- 0xab, 0xa3, 0xc8, 0xa1, 0xa8, 0x68, 0x09, 0xf4, 0x69, 0xdc, 0xf8, 0xf1, 0xa4, 0x12, 0xb1, 0x57,
- 0x23, 0xff, 0x1a, 0x3f, 0x9c, 0xd4, 0xe4, 0x7a, 0x95, 0x38, 0x15, 0xda, 0x24, 0xcf, 0x17, 0x23,
- 0x8f, 0xda, 0x64, 0x60, 0xa4, 0x80, 0x9b, 0x53, 0xa5, 0x1d, 0xed, 0x2e, 0x70, 0x52, 0xfb, 0xd0,
- 0x93, 0x4e, 0x50, 0x2a, 0xcd, 0x4f, 0x62, 0x11, 0x2d, 0x80, 0x9f, 0xb8, 0x62, 0xa2, 0xac, 0xf1,
- 0x23, 0x72, 0xa9, 0xd7, 0x3b, 0x43, 0x4f, 0x2c, 0x8e, 0x84, 0xc2, 0xea, 0x28, 0x9a, 0xa9, 0xa0,
- 0x06, 0x35, 0x9e, 0xd4, 0xe4, 0x53, 0xfe, 0x52, 0x64, 0x37, 0x57, 0x14, 0xe9, 0xd4, 0xfa, 0xbc,
- 0x76, 0x10, 0x69, 0x18, 0xed, 0x2e, 0x46, 0xf7, 0x16, 0xaf, 0x00, 0x85, 0xfa, 0x78, 0x2d, 0x57,
- 0x5e, 0x69, 0x04, 0x11, 0x32, 0x0a, 0x4c, 0x15, 0x01, 0xbc, 0x31, 0xfd, 0xd6, 0xd7, 0x9b, 0x81,
- 0xa1, 0x80, 0x2d, 0x38, 0xad, 0x85, 0x47, 0xc2, 0xd4, 0x55, 0xc6, 0x85, 0x03, 0x5f, 0x70, 0xaa,
- 0xcc, 0xbf, 0x97, 0xbb, 0x02, 0x67, 0x3b, 0xe1, 0xd5, 0x59, 0x80, 0x11, 0x67, 0x65, 0x19, 0xd0,
- 0xd4, 0xf0, 0xab, 0xfc, 0x49, 0x0b, 0x0e, 0x88, 0x74, 0xc2, 0x38, 0xbb, 0xf5, 0x05, 0xc1, 0xc9,
- 0xdc, 0xe0, 0x9f, 0xac, 0xe3, 0x02, 0xf4, 0x3c, 0x0b, 0x34, 0x2e, 0xfa, 0xf2, 0x05, 0x39, 0xae,
- 0xfe, 0x6b, 0xc0, 0x82, 0x36, 0xcb, 0xde, 0x69, 0x90, 0x8d, 0xa6, 0xd5, 0x4f, 0x8c, 0x89, 0x0c,
- 0x49, 0x3a, 0x52, 0x89, 0xff, 0x23, 0x19, 0x71, 0xf2, 0xea, 0xc8, 0x3c, 0xd6, 0x5a, 0x49, 0x4f,
- 0x4d, 0xb8, 0x6d, 0x6a, 0x8d, 0x71, 0xc4, 0x86, 0x99, 0x77, 0x0a, 0xbb, 0x1f, 0x82, 0x29, 0xa5,
- 0x38, 0xe2, 0x37, 0x4c, 0x24, 0x93, 0x7f, 0x3d, 0x69, 0x01, 0x85, 0xa1, 0x97, 0x97, 0x48, 0xbe,
- 0x96, 0xb0, 0x4f, 0x4b, 0xd5, 0x9e, 0x44, 0x94, 0xf9, 0x42, 0x47, 0x08, 0xe4, 0x7c, 0x22, 0x41,
- 0xd1, 0x9a, 0x80, 0x78, 0xca, 0x8a, 0x26, 0xd1, 0xf8, 0xa1, 0xa5, 0x6a, 0x16, 0xea, 0xc2, 0x2c,
- 0x5e, 0xc8, 0x02, 0x07, 0x09, 0xc4, 0x40, 0x57, 0x90, 0x76, 0xbe, 0x36, 0xa5, 0xa2, 0xb6, 0x5d,
- 0x15, 0x2e, 0x9c, 0x19, 0x49, 0x8f, 0xad, 0x15, 0x1c, 0xee, 0x7c, 0xee, 0x56, 0x8e, 0x34, 0x09,
- 0x88, 0xba, 0x98, 0x18, 0x80, 0xa3, 0xb5, 0x4e, 0xd1, 0xd1, 0x65, 0x3b, 0x92, 0xfd, 0xa6, 0x9e,
- 0x20, 0x9b, 0x1e, 0xf2, 0x3a, 0x2a, 0xfb, 0xfb, 0x53, 0xf6, 0x9f, 0x8d, 0x12, 0xbd, 0x4f, 0x2b,
- 0x62, 0x5b, 0x8b, 0xa6, 0xce, 0xea, 0xa8, 0x57, 0xf6, 0x81, 0x3d, 0x14, 0x4f, 0xd0, 0x29, 0x4b,
- 0xec, 0x56, 0x4d, 0xe8, 0x51, 0x32, 0x99, 0xd4, 0x9d, 0x51, 0x2b, 0xf2, 0xab, 0x0f, 0xaa, 0x15,
- 0x30, 0xa6, 0xd3, 0x6a, 0x52, 0x6e, 0xdd, 0x56, 0x8b, 0x0a, 0x56, 0xf7, 0x80, 0x57, 0x38, 0x49,
- 0xeb, 0x28, 0x2e, 0xf2, 0xab, 0x29, 0x2e, 0x60, 0x4c, 0x14, 0x4b, 0xb9, 0x75, 0x14, 0xab, 0x60,
- 0xdf, 0xf4, 0xb1, 0x36, 0x6c, 0x4a, 0xcd, 0x09, 0xa9, 0x22, 0xbf, 0xfa, 0xc5, 0x5d, 0x01, 0x62,
- 0x7c, 0x75, 0xb7, 0xc8, 0xad, 0x7d, 0x5f, 0x4d, 0x01, 0x5b, 0x47, 0x70, 0xf5, 0x09, 0x46, 0x91,
- 0x5d, 0x4f, 0x6e, 0xd5, 0x59, 0xc6, 0x22, 0x73, 0x2d, 0xb1, 0xca, 0x4e, 0xd0, 0x26, 0x76, 0xaa,
- 0x34, 0xe1, 0x8a, 0x9f, 0xa6, 0x73, 0x5d, 0x45, 0xae, 0xab, 0x4e, 0x43, 0xd3, 0x09, 0x2f, 0x19,
- 0xd5, 0x4e, 0x53, 0xa9, 0xf8, 0x69, 0xa2, 0xa5, 0xc8, 0x75, 0xd5, 0x09, 0x66, 0xa2, 0x45, 0x46,
- 0xb5, 0xfd, 0x24, 0x39, 0xf8, 0x81, 0x30, 0x89, 0xf7, 0x8b, 0x9f, 0xa6, 0x37, 0x7f, 0x45, 0xa6,
- 0xab, 0x4e, 0x08, 0xe3, 0xdb, 0xbf, 0x12, 0xa6, 0x5d, 0xb8, 0x5a, 0xfc, 0xaa, 0x22, 0x84, 0x1d,
- 0xc5, 0x93, 0x18, 0xbd, 0x92, 0x0c, 0xb6, 0x67, 0x69, 0x60, 0xd7, 0xa3, 0x3f, 0xe3, 0xe8, 0xcf,
- 0x38, 0xfa, 0x33, 0x8e, 0xfe, 0x8c, 0xa3, 0x3f, 0xe3, 0x65, 0xf8, 0x33, 0xf8, 0x19, 0xe4, 0xe2,
- 0x44, 0x9c, 0x72, 0x80, 0x8e, 0x32, 0xc1, 0xb3, 0x72, 0x79, 0x14, 0xef, 0x5d, 0x65, 0xf3, 0x74,
- 0x12, 0x42, 0xeb, 0x1f, 0xe0, 0x03, 0x61, 0x06, 0x10, 0x3d, 0xff, 0x46, 0xda, 0xd6, 0x5e, 0xc2,
- 0xd5, 0x2e, 0xbc, 0xa1, 0x46, 0xad, 0x20, 0xba, 0x69, 0x28, 0x75, 0x90, 0x4b, 0x5f, 0x15, 0xad,
- 0xaa, 0x1f, 0xdd, 0x2a, 0xfc, 0x0e, 0xd6, 0xe5, 0xfb, 0x9f, 0xfb, 0x9f, 0xff, 0xd1, 0x78, 0xd3,
- 0xee, 0x9c, 0x94, 0x4d, 0x4d, 0x58, 0x5d, 0xfd, 0x3a, 0x07, 0x8c, 0xd9, 0x18, 0x94, 0x8f, 0xc6,
- 0x6c, 0x7b, 0x3e, 0x4f, 0xb3, 0x73, 0x1f, 0xd3, 0x14, 0x3c, 0xac, 0x19, 0x77, 0x40, 0x13, 0xec,
- 0x50, 0x56, 0xd4, 0x41, 0x4d, 0xa0, 0x7d, 0x5a, 0x30, 0xb5, 0x6c, 0xb2, 0x9d, 0x01, 0x52, 0x6f,
- 0x7f, 0x6c, 0x65, 0x3f, 0xec, 0xe3, 0xbc, 0x9d, 0x34, 0x7a, 0xdb, 0x29, 0xf2, 0x75, 0x43, 0xb3,
- 0xb1, 0x2e, 0x4e, 0x61, 0x3e, 0xbc, 0xff, 0xe5, 0x43, 0xff, 0x97, 0x77, 0xbf, 0xfd, 0xf6, 0x8e,
- 0x68, 0x0c, 0xdd, 0xce, 0xb9, 0x67, 0x0e, 0xde, 0xc7, 0xc5, 0x38, 0x9b, 0xe5, 0xa0, 0x7d, 0x97,
- 0x45, 0x3f, 0x9b, 0xed, 0xe2, 0xf6, 0x3c, 0x2a, 0x8d, 0x3c, 0x71, 0x25, 0x4b, 0x70, 0xde, 0x51,
- 0x2c, 0xb3, 0x50, 0xc4, 0x37, 0x54, 0x7c, 0x8a, 0x28, 0x12, 0x74, 0xd4, 0xa4, 0xe1, 0x97, 0xa5,
- 0x1d, 0x12, 0x17, 0x14, 0x4d, 0xf4, 0x2c, 0x81, 0xdf, 0x51, 0x24, 0xa1, 0xed, 0xcb, 0x27, 0x2e,
- 0xaf, 0x47, 0xa3, 0xc0, 0x42, 0x7d, 0xba, 0x85, 0x37, 0x24, 0x98, 0x6e, 0x2d, 0xb4, 0x75, 0xe8,
- 0x4b, 0x7e, 0x50, 0x27, 0x90, 0xd8, 0x85, 0x56, 0x67, 0x89, 0xae, 0x76, 0xba, 0xed, 0xee, 0x5b,
- 0x8f, 0x0a, 0xf1, 0x66, 0x41, 0x85, 0x5d, 0x43, 0x06, 0xad, 0x1b, 0x6a, 0x2e, 0x46, 0xde, 0x2b,
- 0x16, 0x5e, 0x86, 0xcc, 0xbf, 0x97, 0xc8, 0xc5, 0xe3, 0x82, 0x26, 0x8f, 0x32, 0xf8, 0x93, 0xdb,
- 0x61, 0xb9, 0x43, 0x68, 0x5d, 0xc3, 0x00, 0x3f, 0x1c, 0x79, 0x3d, 0x50, 0x74, 0x21, 0xf4, 0x66,
- 0x4b, 0x07, 0x0c, 0x81, 0xb4, 0x96, 0x8c, 0xc0, 0x85, 0x94, 0xa6, 0x92, 0xb2, 0xe1, 0x69, 0x42,
- 0x47, 0x5d, 0xf1, 0x1e, 0xe4, 0xed, 0xfe, 0x7f, 0xa7, 0x8d, 0x81, 0x68
+ 0x2e, 0x36, 0xed, 0xbd, 0xc3, 0x15, 0x0b, 0x43, 0xb2, 0x15, 0x47, 0xef, 0x6c, 0xcb, 0xcf, 0x56,
+ 0x12, 0x3b, 0x87, 0xfd, 0xdf, 0x1f, 0x67, 0xf8, 0x4d, 0x51, 0xf2, 0x47, 0x62, 0x6f, 0x36, 0x67,
+ 0x14, 0xdd, 0x58, 0xe4, 0x70, 0x38, 0x24, 0x87, 0xc3, 0x99, 0x21, 0x39, 0xfc, 0x3e, 0xbb, 0x1a,
+ 0xa6, 0x57, 0x8d, 0x5f, 0x7e, 0xed, 0xbf, 0xbb, 0x3c, 0x99, 0xcd, 0xd3, 0x41, 0xb6, 0xc8, 0xf2,
+ 0x69, 0xe3, 0x3a, 0x1b, 0x5d, 0xcf, 0x1a, 0x57, 0xe3, 0x3c, 0x2e, 0xc2, 0x93, 0xef, 0xd3, 0xf1,
+ 0x22, 0x3d, 0xf9, 0x3e, 0xbb, 0x6a, 0x7c, 0x47, 0x60, 0xb3, 0x69, 0x3a, 0x74, 0xc6, 0xf9, 0xdd,
+ 0xcc, 0x3d, 0xf9, 0x9e, 0x7e, 0x36, 0xe0, 0x8b, 0x40, 0x4d, 0x87, 0xd9, 0x95, 0x0e, 0x36, 0x49,
+ 0x87, 0xd9, 0xcd, 0x44, 0x81, 0x64, 0x09, 0x56, 0x60, 0xac, 0x53, 0x82, 0xe2, 0xa7, 0x00, 0xa4,
+ 0x7f, 0x6e, 0xd3, 0x41, 0xaf, 0x71, 0x33, 0x9d, 0xc5, 0x83, 0x7f, 0xf5, 0x91, 0x38, 0x67, 0x90,
+ 0x4f, 0x17, 0x05, 0x25, 0xb4, 0x01, 0xc9, 0xe9, 0xf0, 0xef, 0xf1, 0xf8, 0x26, 0x75, 0x1b, 0xff,
+ 0xce, 0xa6, 0x3c, 0xe5, 0x62, 0x5a, 0x60, 0x62, 0x44, 0x92, 0x1c, 0x15, 0x28, 0x04, 0x98, 0xdb,
+ 0x4e, 0xa4, 0x83, 0x05, 0xbd, 0xf3, 0x17, 0xe1, 0x3c, 0x2d, 0x6e, 0xe6, 0xd3, 0x06, 0x54, 0xe8,
+ 0xdc, 0x76, 0x7c, 0x1d, 0xa2, 0x75, 0xdb, 0xf1, 0x08, 0x90, 0x1b, 0x7e, 0x51, 0x09, 0xca, 0xc9,
+ 0xbf, 0x59, 0xb1, 0xb2, 0x90, 0xf4, 0x81, 0xe6, 0x30, 0xa2, 0xc8, 0xff, 0x2c, 0x41, 0x21, 0x88,
+ 0x83, 0x04, 0x3d, 0xad, 0x6a, 0xda, 0x48, 0x59, 0xc2, 0x0d, 0xba, 0xbd, 0x97, 0xed, 0x8e, 0x3f,
+ 0xc9, 0x87, 0x7a, 0x41, 0xbf, 0xd7, 0xee, 0xb8, 0x94, 0xa0, 0xb3, 0xc6, 0x30, 0x1d, 0xe4, 0xc3,
+ 0xb4, 0x3f, 0xc8, 0xc7, 0xf9, 0x9c, 0x91, 0x83, 0x84, 0xa6, 0x53, 0x48, 0x1f, 0xfe, 0x04, 0xe9,
+ 0x84, 0x18, 0x59, 0xd1, 0x99, 0xa3, 0x75, 0xaa, 0x0a, 0xf7, 0x67, 0xe7, 0x33, 0x21, 0xea, 0xfc,
+ 0x9c, 0x54, 0x5a, 0x0d, 0xd3, 0xe5, 0x30, 0x27, 0x84, 0x04, 0xda, 0x72, 0x06, 0x3c, 0xc9, 0x96,
+ 0x7d, 0x6c, 0x89, 0x42, 0x86, 0x32, 0x04, 0xbe, 0xda, 0x59, 0x85, 0xa4, 0x89, 0x14, 0x53, 0x47,
+ 0x8a, 0x10, 0xe1, 0xab, 0x9f, 0xdd, 0xcf, 0x7e, 0xc1, 0x1b, 0xab, 0x54, 0x64, 0x34, 0xf8, 0x8c,
+ 0xd5, 0x84, 0x34, 0x2e, 0x4a, 0x55, 0x21, 0xc4, 0x24, 0x9b, 0x62, 0x76, 0xa4, 0xf5, 0x19, 0x52,
+ 0xac, 0x16, 0x96, 0x04, 0xb0, 0x6f, 0xd2, 0x62, 0x37, 0xa4, 0x18, 0xe2, 0xe5, 0x46, 0x18, 0x7a,
+ 0x06, 0x86, 0x53, 0xc0, 0xa0, 0x34, 0x97, 0x53, 0xe2, 0x73, 0x84, 0xbc, 0x89, 0xbd, 0xc6, 0x28,
+ 0x2d, 0xfa, 0xb3, 0xb8, 0x28, 0xd2, 0xf9, 0xb4, 0x3f, 0xcb, 0x17, 0x5a, 0x5f, 0x66, 0xcb, 0x74,
+ 0x4c, 0xea, 0xcc, 0xe7, 0xc3, 0xfe, 0xcd, 0x6c, 0x96, 0xce, 0xfd, 0x8a, 0x4c, 0x32, 0x47, 0x8d,
+ 0x4c, 0x86, 0x70, 0x91, 0xdd, 0x1b, 0xc3, 0x90, 0x8d, 0xd3, 0xfe, 0xcd, 0x34, 0x2b, 0x16, 0xfd,
+ 0x22, 0xef, 0x23, 0x8e, 0x85, 0x56, 0x30, 0x5f, 0xd0, 0xde, 0xeb, 0x35, 0xf2, 0xab, 0xab, 0x45,
+ 0x5a, 0x44, 0xc0, 0x8d, 0xfc, 0xff, 0x32, 0x41, 0x6a, 0x45, 0x2e, 0xcc, 0x9b, 0x76, 0xc7, 0x96,
+ 0xd6, 0x2c, 0x53, 0xab, 0x41, 0xf1, 0xbe, 0x72, 0x6c, 0xf4, 0x79, 0x84, 0xa8, 0x26, 0xa5, 0xc6,
+ 0x0d, 0xd4, 0x62, 0xe1, 0x97, 0x93, 0xbf, 0x7c, 0x6f, 0x97, 0x71, 0x4c, 0x16, 0x3d, 0x3d, 0x29,
+ 0xf7, 0x17, 0x42, 0xff, 0x3c, 0x4b, 0x6e, 0x8a, 0x94, 0x76, 0x78, 0x0c, 0x83, 0x1e, 0x92, 0x16,
+ 0x5f, 0xe5, 0xf3, 0x09, 0xe1, 0xb7, 0x82, 0x30, 0x7d, 0x9f, 0xfc, 0x99, 0x67, 0xcb, 0xf0, 0x36,
+ 0xcf, 0x86, 0x24, 0x29, 0x9b, 0x3a, 0x64, 0x4c, 0x46, 0xe3, 0xfe, 0xc7, 0x7c, 0x91, 0x15, 0xa4,
+ 0x75, 0x11, 0x87, 0xf0, 0x70, 0x7a, 0x23, 0x0a, 0xbf, 0xe3, 0x77, 0x5d, 0xe8, 0x10, 0x8e, 0x8a,
+ 0xce, 0x1f, 0xca, 0xb1, 0x02, 0x3f, 0x9b, 0xbe, 0x5c, 0x9c, 0x95, 0x6a, 0xf8, 0x79, 0x1e, 0x8f,
+ 0x28, 0xc3, 0xb3, 0x92, 0x9e, 0x84, 0x3d, 0x61, 0x5d, 0xfd, 0xe1, 0xef, 0xef, 0x3e, 0xbd, 0xfd,
+ 0xf4, 0xe6, 0x7f, 0xfa, 0x17, 0xef, 0x2f, 0x3f, 0xbe, 0xfb, 0xe9, 0xf7, 0x0f, 0x9f, 0x4e, 0xb4,
+ 0x92, 0x48, 0x53, 0x97, 0x48, 0xac, 0x90, 0xb7, 0x59, 0xa1, 0x4a, 0x6f, 0xa0, 0x42, 0x2b, 0x91,
+ 0xb4, 0x7d, 0x75, 0x6c, 0xfb, 0x71, 0x6d, 0x6e, 0x52, 0xca, 0x35, 0x59, 0xb3, 0x0e, 0x00, 0x39,
+ 0xb0, 0xd4, 0x29, 0x8b, 0x41, 0x3c, 0x56, 0xeb, 0xd5, 0xd3, 0x93, 0x52, 0xba, 0x8d, 0x57, 0x43,
+ 0xeb, 0xf0, 0xde, 0xc6, 0xf3, 0x55, 0x36, 0x1d, 0xd1, 0xa4, 0x5b, 0x48, 0x22, 0xd5, 0x58, 0x12,
+ 0x93, 0x1d, 0x86, 0x9c, 0xa1, 0x8b, 0x4c, 0x39, 0x62, 0xe9, 0x12, 0xdf, 0xd2, 0x0b, 0xbe, 0x68,
+ 0xb7, 0x67, 0x0e, 0x80, 0x6f, 0x6f, 0xa2, 0x8f, 0x95, 0xf3, 0x8a, 0x93, 0x07, 0x57, 0x9c, 0x98,
+ 0x15, 0x27, 0x6b, 0x2a, 0xd6, 0x99, 0x5c, 0x65, 0x8d, 0x62, 0x5c, 0xcd, 0x36, 0xc9, 0xbc, 0x3a,
+ 0x8f, 0x94, 0x4b, 0x6a, 0xca, 0x95, 0xf2, 0x8a, 0x74, 0x89, 0xf2, 0xc7, 0xe4, 0x88, 0x89, 0xc2,
+ 0xd3, 0xe6, 0x54, 0xe3, 0xe9, 0x8b, 0x78, 0x32, 0x1b, 0xa7, 0xf3, 0xde, 0x5b, 0x92, 0x97, 0x4d,
+ 0xe2, 0x51, 0xba, 0x2b, 0x77, 0x60, 0x0e, 0x62, 0xc0, 0x5e, 0x45, 0x41, 0xcd, 0x4a, 0xfb, 0x38,
+ 0xfd, 0xb8, 0x40, 0x8f, 0x60, 0x05, 0xd2, 0x3b, 0x29, 0x10, 0x6d, 0xf0, 0xf5, 0x1e, 0x52, 0x32,
+ 0x24, 0x6a, 0xb6, 0x18, 0xa2, 0x40, 0xe8, 0x46, 0x24, 0x9f, 0xc8, 0xea, 0xb4, 0xf7, 0xd6, 0x61,
+ 0x0d, 0xf0, 0x29, 0x3b, 0xe8, 0xe4, 0x10, 0xc6, 0x90, 0x04, 0x25, 0x3a, 0x41, 0xbd, 0x32, 0x45,
+ 0x49, 0x15, 0x45, 0x89, 0x95, 0xa2, 0x7e, 0xa2, 0xd2, 0xd4, 0xb3, 0xd3, 0xd4, 0x73, 0x43, 0x4d,
+ 0x30, 0x41, 0xa5, 0xb4, 0x0d, 0x3e, 0x2d, 0xe6, 0xe3, 0x88, 0xb9, 0x87, 0x91, 0x71, 0x49, 0x9e,
+ 0x8f, 0x85, 0x30, 0xb9, 0xcb, 0x8a, 0x6b, 0x02, 0x30, 0x33, 0x73, 0x67, 0x59, 0x31, 0xb8, 0x2e,
+ 0xe7, 0x32, 0xb6, 0x23, 0x8d, 0x9c, 0xdf, 0x10, 0x2d, 0x04, 0x71, 0x88, 0x4c, 0x58, 0xba, 0x04,
+ 0xb7, 0x0d, 0xd3, 0xdb, 0x6c, 0x90, 0xb2, 0xd9, 0x36, 0x8f, 0x89, 0xe8, 0x10, 0x70, 0x8a, 0xda,
+ 0x0f, 0xeb, 0x42, 0x3c, 0x49, 0xe7, 0x31, 0x4c, 0xae, 0x41, 0x3a, 0x25, 0x9d, 0xdd, 0x1f, 0x66,
+ 0x8b, 0x22, 0x9e, 0x0e, 0xd2, 0xb5, 0x12, 0xec, 0x94, 0xb0, 0xe3, 0x30, 0x2e, 0x62, 0xec, 0xac,
+ 0x29, 0xf4, 0xd6, 0x7f, 0xbf, 0xb9, 0xec, 0xff, 0xf1, 0xfe, 0xe2, 0xe7, 0x0f, 0x9f, 0x7e, 0xeb,
+ 0xb3, 0x75, 0xe3, 0xc4, 0x4a, 0x1d, 0x66, 0xf5, 0x0b, 0xa5, 0x0a, 0x4a, 0x14, 0x0e, 0x65, 0xcc,
+ 0xd6, 0x2a, 0x5e, 0x95, 0x92, 0x45, 0x33, 0xd8, 0x2a, 0xae, 0xb7, 0x47, 0x5b, 0xe6, 0x94, 0x45,
+ 0xd9, 0x42, 0xd8, 0x3c, 0x26, 0x2b, 0xf7, 0xc2, 0x4e, 0x19, 0xcd, 0xd3, 0x48, 0xe3, 0x2a, 0x04,
+ 0xeb, 0x05, 0x0a, 0x21, 0xa8, 0xd3, 0x14, 0x8c, 0x06, 0xcb, 0x34, 0x28, 0xd4, 0x61, 0x78, 0x25,
+ 0x6b, 0xa8, 0x4c, 0xc6, 0x37, 0x15, 0xbd, 0x07, 0x39, 0x1a, 0x85, 0x98, 0xcb, 0xc8, 0x83, 0x4c,
+ 0x41, 0x9c, 0x52, 0x0c, 0xd3, 0x0d, 0xba, 0x4a, 0x58, 0xd7, 0x90, 0xc4, 0x66, 0x87, 0xbd, 0x3c,
+ 0xcb, 0xac, 0x24, 0x4c, 0xa8, 0x1a, 0x65, 0xda, 0xe4, 0xac, 0xab, 0x26, 0x4f, 0x81, 0xa9, 0xa1,
+ 0x70, 0x51, 0xcc, 0xf3, 0x7f, 0xa5, 0x75, 0xac, 0xa7, 0x42, 0x54, 0x73, 0xa0, 0x0a, 0x65, 0x63,
+ 0x44, 0x2d, 0xbf, 0x8e, 0x1f, 0x4d, 0xc0, 0xf5, 0xb4, 0xdf, 0x65, 0xc3, 0xe2, 0xba, 0x96, 0x76,
+ 0x84, 0xa8, 0x63, 0x51, 0x15, 0xae, 0x82, 0x51, 0x35, 0x90, 0x35, 0xec, 0x6a, 0xc2, 0xae, 0x6f,
+ 0x43, 0x2d, 0xa3, 0xe8, 0x30, 0x95, 0xfc, 0xa2, 0x83, 0xd9, 0xd8, 0xc6, 0x80, 0xa8, 0xe3, 0x9e,
+ 0x32, 0x28, 0x75, 0x35, 0x88, 0x75, 0x14, 0x7e, 0x91, 0xb5, 0xb4, 0x56, 0x94, 0x0d, 0xa8, 0x42,
+ 0x6c, 0x9a, 0xa2, 0x4c, 0x5e, 0xf9, 0x42, 0xac, 0xb9, 0x9c, 0x14, 0x53, 0x72, 0x45, 0x5b, 0xc9,
+ 0x27, 0xfa, 0x27, 0x32, 0x6d, 0x6c, 0x2e, 0x81, 0x7c, 0x29, 0xac, 0x44, 0x85, 0x36, 0x61, 0x14,
+ 0x6d, 0x23, 0x6f, 0xe0, 0x1f, 0x4b, 0x8d, 0x90, 0xec, 0x73, 0xc1, 0x23, 0x6a, 0x33, 0xa4, 0x4b,
+ 0xb4, 0x95, 0x0c, 0x61, 0x7f, 0x2d, 0x95, 0xb1, 0x1c, 0x5f, 0x11, 0x29, 0xb6, 0x2a, 0x05, 0x82,
+ 0x5d, 0x44, 0x83, 0xfa, 0x61, 0x1b, 0x51, 0x35, 0xdf, 0x37, 0x85, 0x86, 0x6d, 0x7c, 0x75, 0x84,
+ 0x3b, 0x4f, 0x7b, 0xf5, 0xc3, 0xd2, 0x35, 0x6a, 0xb6, 0x6f, 0xca, 0x83, 0x0a, 0x2e, 0xd0, 0x51,
+ 0x3e, 0x60, 0x32, 0xeb, 0x9f, 0xd5, 0xd4, 0xc9, 0xf1, 0x33, 0x67, 0xba, 0x6d, 0x18, 0x4d, 0xac,
+ 0x95, 0x53, 0x15, 0x7d, 0x5b, 0x54, 0xf1, 0x89, 0xb0, 0x42, 0x50, 0x2b, 0xa9, 0xfd, 0x03, 0x6e,
+ 0x31, 0x8f, 0xfc, 0xd3, 0x92, 0xda, 0xe5, 0x20, 0x9b, 0x0f, 0x88, 0x7e, 0x45, 0x75, 0x9a, 0x88,
+ 0x54, 0x85, 0xc3, 0x4a, 0x80, 0xbd, 0x4e, 0xfb, 0xdc, 0x0d, 0x89, 0xb1, 0xee, 0x98, 0x5a, 0x16,
+ 0x57, 0xa5, 0x07, 0xf9, 0x7c, 0x4a, 0xf4, 0xa0, 0x19, 0xb7, 0xb8, 0x34, 0x54, 0xac, 0xa4, 0xae,
+ 0xbd, 0x91, 0x92, 0x46, 0xa1, 0x66, 0xc4, 0x48, 0xf5, 0x1c, 0x3a, 0xf9, 0x9a, 0x6a, 0xbf, 0x83,
+ 0xa6, 0xa9, 0xeb, 0x70, 0x5f, 0x1a, 0xd0, 0x2f, 0xcc, 0x5d, 0x35, 0x9b, 0xe7, 0xff, 0x9b, 0x0e,
+ 0x8a, 0x74, 0xc8, 0xc9, 0xd7, 0x6d, 0x3e, 0x8d, 0x1e, 0x6a, 0xfb, 0x3d, 0xac, 0x76, 0xcf, 0x31,
+ 0x6b, 0x6c, 0xdf, 0x05, 0x35, 0x9a, 0x21, 0xb1, 0xbc, 0xaa, 0x2d, 0x52, 0x83, 0x14, 0xe6, 0x8e,
+ 0x60, 0xcd, 0xab, 0x29, 0x56, 0x6e, 0x54, 0x45, 0x4f, 0x2b, 0x38, 0xda, 0xcb, 0xd5, 0xb6, 0x4d,
+ 0xad, 0x53, 0x78, 0x2d, 0x54, 0xee, 0x50, 0x83, 0x5a, 0xfa, 0x2e, 0xfc, 0xf2, 0x45, 0xe1, 0xf5,
+ 0x78, 0x5a, 0x64, 0xf1, 0x38, 0x8b, 0x17, 0x28, 0x2e, 0x09, 0xb3, 0x06, 0x36, 0x45, 0x3d, 0xb0,
+ 0xd6, 0x13, 0x52, 0x65, 0x1b, 0x58, 0xff, 0xd4, 0x61, 0x35, 0xb6, 0x97, 0x3e, 0xff, 0xb5, 0xf2,
+ 0x35, 0xe4, 0x68, 0x1c, 0x6f, 0xad, 0xab, 0xef, 0x5b, 0xe3, 0x3e, 0x8c, 0xce, 0xbc, 0x67, 0xe5,
+ 0xf7, 0x70, 0xfa, 0xeb, 0xd7, 0xd0, 0x3b, 0xbf, 0xa6, 0xb6, 0xb8, 0x67, 0x15, 0x8f, 0xa9, 0x77,
+ 0x55, 0xcc, 0xbf, 0x89, 0xb2, 0x56, 0xc9, 0xd9, 0x1b, 0x2b, 0x5e, 0x15, 0x3c, 0xbb, 0x5e, 0x95,
+ 0xaa, 0x66, 0xc8, 0x4d, 0x75, 0xa2, 0x35, 0xdc, 0xb6, 0xbd, 0x32, 0xb3, 0x86, 0x95, 0x76, 0x52,
+ 0x43, 0xd6, 0x32, 0xc9, 0x83, 0x55, 0x07, 0x94, 0x83, 0x44, 0xae, 0x87, 0x14, 0x09, 0x17, 0xde,
+ 0xe3, 0x74, 0x3a, 0x22, 0x94, 0xd1, 0x3f, 0x5c, 0xc0, 0xba, 0x61, 0xa5, 0xf4, 0x66, 0x78, 0xee,
+ 0x43, 0x23, 0x93, 0xac, 0xa1, 0x98, 0xdf, 0x9a, 0xc4, 0x4b, 0x07, 0xf5, 0x66, 0x43, 0x30, 0x6b,
+ 0x23, 0xd5, 0x2f, 0xa2, 0xc5, 0x24, 0xcf, 0x8b, 0xeb, 0x45, 0x91, 0xce, 0x9c, 0x4e, 0xbb, 0xe3,
+ 0x9b, 0x88, 0x7c, 0x9d, 0x40, 0xaa, 0xe2, 0x50, 0x1c, 0x4c, 0x1d, 0x8d, 0xd4, 0xbe, 0x6c, 0xfc,
+ 0xad, 0x41, 0xb0, 0x74, 0x1b, 0x3f, 0xc2, 0x9f, 0xc6, 0x0f, 0x0d, 0x05, 0x7b, 0x09, 0x33, 0x54,
+ 0x67, 0x60, 0xa7, 0x0c, 0x6b, 0x5f, 0x81, 0x0c, 0x2f, 0x9a, 0x68, 0x81, 0x27, 0xfc, 0x69, 0x1e,
+ 0x57, 0xfd, 0x54, 0x8e, 0xf1, 0x0c, 0xb5, 0x50, 0x51, 0xa2, 0x1f, 0xe0, 0x63, 0xdb, 0xf7, 0xee,
+ 0x49, 0xf5, 0x86, 0x88, 0x24, 0x67, 0xad, 0xab, 0x8f, 0x8b, 0x26, 0xbb, 0xbf, 0xae, 0xca, 0x31,
+ 0xcc, 0xd3, 0x33, 0xa2, 0x9d, 0x4c, 0x17, 0x90, 0xb3, 0xc9, 0x5e, 0x02, 0xab, 0xa3, 0x6a, 0x81,
+ 0xbf, 0x4b, 0xc9, 0xf4, 0x2e, 0xec, 0x92, 0x93, 0xe6, 0xd9, 0x9d, 0x21, 0x50, 0x1b, 0xcd, 0x37,
+ 0xdc, 0x20, 0xb4, 0x30, 0xcb, 0xb2, 0xae, 0x44, 0x3a, 0xfa, 0x43, 0xf8, 0xe4, 0xb6, 0x53, 0x21,
+ 0xe8, 0x6e, 0xa7, 0x4a, 0xeb, 0x3f, 0xdf, 0x7d, 0xfa, 0x80, 0x6a, 0x19, 0x6e, 0x73, 0x07, 0xdd,
+ 0x17, 0xed, 0x4e, 0x28, 0x36, 0xf1, 0x7e, 0x79, 0xf3, 0xc7, 0xe5, 0x65, 0xff, 0xa7, 0x0f, 0xef,
+ 0x7e, 0x26, 0x53, 0xeb, 0xf4, 0xf5, 0xab, 0xd7, 0x67, 0xbd, 0xde, 0xab, 0xce, 0x59, 0xa7, 0x7b,
+ 0x76, 0xda, 0x7b, 0xb9, 0xb1, 0x27, 0x81, 0x8d, 0x03, 0xfd, 0x63, 0xb1, 0xa1, 0x68, 0x86, 0x2f,
+ 0x07, 0xc5, 0x30, 0x36, 0xd5, 0x6e, 0x8f, 0xb6, 0xeb, 0x5b, 0xfb, 0x1a, 0xf5, 0xe8, 0xde, 0x05,
+ 0x76, 0x5a, 0x02, 0x79, 0x7d, 0xd8, 0xdf, 0xc8, 0x62, 0xa3, 0x08, 0x2f, 0xa3, 0xc5, 0xff, 0xcd,
+ 0x0b, 0xa7, 0x45, 0x92, 0xbd, 0x71, 0x3e, 0x72, 0x60, 0x34, 0x02, 0xda, 0xc0, 0x40, 0x99, 0x0d,
+ 0x81, 0x1c, 0x08, 0xd7, 0x0d, 0x4e, 0xc9, 0x10, 0x09, 0xe6, 0x8f, 0x2e, 0x3d, 0xb3, 0xe2, 0xd0,
+ 0x10, 0xfe, 0x5c, 0x97, 0xa7, 0x44, 0x97, 0xec, 0x30, 0x6a, 0x7f, 0xe5, 0x0b, 0x3a, 0xcb, 0x4d,
+ 0xb3, 0xb1, 0xc9, 0x80, 0xa9, 0x94, 0xb0, 0x8a, 0x12, 0x98, 0x99, 0x8a, 0x5c, 0xd0, 0x67, 0x82,
+ 0x9c, 0xd1, 0x3b, 0x4d, 0xde, 0xc7, 0x9a, 0x80, 0x5b, 0x32, 0x74, 0xa5, 0xde, 0xc4, 0xc8, 0xda,
+ 0x84, 0x31, 0x69, 0xee, 0x30, 0x6a, 0x91, 0x6e, 0xf4, 0xc8, 0x98, 0xe1, 0xff, 0xc3, 0xbc, 0x70,
+ 0x44, 0xdb, 0x7d, 0xf1, 0x8b, 0xf3, 0xc3, 0x6d, 0x3c, 0x8e, 0x28, 0x1a, 0x4f, 0xe9, 0x3a, 0x4f,
+ 0x92, 0xed, 0xa5, 0xcb, 0x99, 0x33, 0x34, 0x96, 0x25, 0x1c, 0x38, 0x52, 0x14, 0x76, 0x9a, 0xf8,
+ 0xff, 0xee, 0x01, 0xf6, 0xac, 0xef, 0xf2, 0xf9, 0x78, 0xb8, 0xe9, 0xae, 0xef, 0x56, 0x6b, 0x92,
+ 0xc7, 0x90, 0x2b, 0xdb, 0xbc, 0xed, 0x65, 0x14, 0xd3, 0xbf, 0xec, 0x7b, 0x05, 0x62, 0xab, 0x45,
+ 0xd3, 0x56, 0x2a, 0x03, 0x96, 0x77, 0x19, 0x6d, 0x39, 0x74, 0x4d, 0x9e, 0x93, 0x94, 0xea, 0x13,
+ 0x02, 0x6b, 0x1a, 0xc1, 0x8e, 0x95, 0x58, 0x76, 0xe0, 0x10, 0xd8, 0x6d, 0xcf, 0x95, 0x8d, 0x3a,
+ 0x0d, 0x4a, 0x56, 0xee, 0xa3, 0x94, 0x28, 0x7c, 0x98, 0x6b, 0xc6, 0xb0, 0x3e, 0xe8, 0x10, 0x42,
+ 0x67, 0x13, 0xe5, 0xa1, 0x94, 0x18, 0x4f, 0x07, 0xd7, 0xf9, 0xdc, 0x9e, 0xc7, 0x27, 0x6c, 0x19,
+ 0xd3, 0x38, 0x1e, 0xa4, 0x16, 0x3e, 0x58, 0x5c, 0x67, 0x57, 0x45, 0xb8, 0x11, 0x27, 0xd5, 0x6b,
+ 0x0b, 0xd5, 0xee, 0x0b, 0x3e, 0x42, 0x6c, 0xf6, 0x70, 0x52, 0xcc, 0xe4, 0x69, 0x5e, 0xfc, 0xb1,
+ 0x80, 0x74, 0x63, 0x0f, 0x59, 0xf1, 0x3b, 0x7d, 0xcc, 0xc9, 0x74, 0x2b, 0xb1, 0xa2, 0xec, 0x0f,
+ 0xca, 0x89, 0xea, 0xbc, 0x97, 0x54, 0x31, 0x28, 0x4e, 0x55, 0xa4, 0x23, 0x6d, 0xdf, 0xe9, 0xc5,
+ 0xf2, 0xf1, 0x18, 0xcf, 0xe9, 0xf4, 0x67, 0xe9, 0x7c, 0x31, 0x23, 0x70, 0xd9, 0x6d, 0x4a, 0xbd,
+ 0x20, 0xd1, 0x60, 0x4c, 0x38, 0x82, 0x0c, 0xdd, 0x79, 0x13, 0x64, 0x86, 0x53, 0xd3, 0xf2, 0xa0,
+ 0xb2, 0x76, 0x17, 0x75, 0xdc, 0x33, 0x18, 0xfe, 0xb5, 0x8a, 0x1f, 0x17, 0x15, 0xa6, 0xf7, 0xc7,
+ 0x11, 0xa3, 0xdd, 0x64, 0xc3, 0xb8, 0xc6, 0xe7, 0xe3, 0xd5, 0xb4, 0x2a, 0xe4, 0xc3, 0x12, 0x71,
+ 0x56, 0xc1, 0x29, 0xcc, 0xc6, 0x44, 0x26, 0xae, 0x54, 0xff, 0xcd, 0x2e, 0x03, 0xca, 0xac, 0x92,
+ 0xf1, 0xec, 0x3a, 0x8e, 0x48, 0xff, 0x85, 0x56, 0x29, 0x87, 0x8d, 0xe6, 0x0d, 0xf7, 0x10, 0x18,
+ 0xbd, 0x6e, 0xbc, 0xaa, 0xc6, 0x7f, 0x35, 0x60, 0x2a, 0xda, 0x14, 0x60, 0xa5, 0x24, 0x4e, 0x57,
+ 0x56, 0xfa, 0x0b, 0x2d, 0xce, 0x48, 0xb2, 0x96, 0xf7, 0xa2, 0x76, 0x37, 0xfc, 0x72, 0x90, 0x59,
+ 0xf8, 0xa4, 0xa6, 0x9b, 0x91, 0x6e, 0x6c, 0x23, 0x1b, 0x8a, 0x80, 0x3d, 0x99, 0x2b, 0x29, 0xc7,
+ 0x99, 0x6b, 0x9d, 0xb9, 0x2a, 0x7d, 0xb3, 0x78, 0x38, 0x24, 0x1d, 0xd8, 0xbf, 0x8a, 0x07, 0x45,
+ 0x0e, 0xbe, 0xd6, 0x5e, 0x69, 0x62, 0x0b, 0xfe, 0x29, 0x4d, 0x67, 0xbd, 0xf0, 0x3e, 0x66, 0x37,
+ 0x1f, 0xff, 0x28, 0x4e, 0x16, 0x52, 0xc0, 0xb4, 0x57, 0xae, 0xa2, 0xcc, 0x4a, 0xfa, 0x74, 0x7a,
+ 0x42, 0x83, 0x1f, 0xa2, 0x2d, 0x7c, 0xdc, 0xb5, 0xd4, 0x2b, 0x6a, 0x83, 0x58, 0xfd, 0x6f, 0x09,
+ 0x14, 0x7a, 0xe0, 0x79, 0xdd, 0x4f, 0x83, 0xeb, 0x4d, 0xf1, 0xa6, 0xa8, 0x16, 0x5b, 0x0b, 0xb7,
+ 0xc1, 0xae, 0x52, 0x6d, 0xc0, 0xc4, 0x59, 0x8f, 0x1f, 0x87, 0xd6, 0x08, 0x36, 0x3c, 0x4a, 0x46,
+ 0x73, 0xdc, 0x5a, 0xf7, 0x93, 0xd4, 0x85, 0x3d, 0x1b, 0xce, 0xd0, 0xe2, 0x52, 0xeb, 0x12, 0x8b,
+ 0xd5, 0x0a, 0x1c, 0x58, 0x46, 0x51, 0xb3, 0xe0, 0xf8, 0xa8, 0xd4, 0x55, 0xc5, 0xf9, 0x07, 0x58,
+ 0x2a, 0x1d, 0x8e, 0x52, 0xe4, 0x5b, 0xab, 0xef, 0xa8, 0xd6, 0xc1, 0xd5, 0xd2, 0x36, 0x30, 0xa1,
+ 0xa7, 0x5b, 0x26, 0xe6, 0x4a, 0x2f, 0x13, 0x75, 0x3f, 0x7e, 0x39, 0xdc, 0xc1, 0x59, 0xcb, 0x2e,
+ 0xc7, 0xda, 0x63, 0xb2, 0x5b, 0xd2, 0xf7, 0xad, 0x9d, 0x9c, 0x7a, 0x4a, 0x07, 0x80, 0x4a, 0x06,
+ 0xea, 0x57, 0x3f, 0xb7, 0x71, 0x80, 0xa3, 0x0d, 0x9b, 0xf2, 0xf0, 0x57, 0xd9, 0xe5, 0x7b, 0xe4,
+ 0x1d, 0xb2, 0x3d, 0xef, 0xdc, 0x6c, 0xb5, 0x7d, 0x62, 0xb1, 0x45, 0x1f, 0xe5, 0xa8, 0xe8, 0x16,
+ 0xb2, 0xc2, 0xea, 0x6f, 0xb0, 0x58, 0xe5, 0x55, 0x83, 0x73, 0x53, 0x8c, 0xb3, 0x69, 0xed, 0xd1,
+ 0x38, 0x0d, 0xa4, 0x5a, 0xc6, 0x68, 0x60, 0x36, 0x59, 0xa3, 0x03, 0xd4, 0x71, 0x55, 0x09, 0xf2,
+ 0x19, 0xc9, 0x1e, 0xbd, 0xc3, 0xb5, 0x2f, 0x9b, 0x2c, 0xd2, 0x00, 0xfc, 0xd2, 0x60, 0xd8, 0x64,
+ 0x93, 0x81, 0x73, 0xa7, 0xde, 0xfc, 0xda, 0xb2, 0x0a, 0x79, 0x36, 0x72, 0x74, 0xdd, 0x3c, 0xd0,
+ 0x34, 0xee, 0x26, 0x4c, 0x9a, 0x00, 0xbc, 0xc2, 0x9c, 0xeb, 0xf5, 0xb3, 0x0d, 0x5b, 0x31, 0xfe,
+ 0xc1, 0xd9, 0xf5, 0x70, 0xc2, 0x50, 0x6f, 0xe8, 0x2e, 0x7c, 0xf2, 0x40, 0x21, 0x29, 0x55, 0x45,
+ 0xa9, 0xc7, 0x92, 0x81, 0x69, 0x09, 0x31, 0x98, 0xcf, 0x89, 0x41, 0xb4, 0xe2, 0x1a, 0x22, 0xd5,
+ 0xde, 0xc1, 0x6f, 0x69, 0x6c, 0x81, 0x82, 0xf6, 0x0d, 0x58, 0x4c, 0x55, 0x50, 0x25, 0xdb, 0x73,
+ 0xb0, 0x38, 0x97, 0xc2, 0x87, 0xf3, 0xf0, 0x3e, 0xde, 0x75, 0x23, 0x7e, 0x52, 0x03, 0xad, 0x9b,
+ 0x87, 0x5e, 0x17, 0xda, 0x7c, 0x3a, 0x3c, 0xf9, 0x33, 0xdc, 0xfc, 0xd2, 0xc7, 0xd5, 0x3c, 0x9f,
+ 0xd8, 0xf1, 0xa8, 0x10, 0x56, 0x5a, 0x61, 0x99, 0x52, 0x81, 0x74, 0x82, 0xd9, 0xad, 0x51, 0x25,
+ 0xdb, 0x46, 0x34, 0x1b, 0x1e, 0x13, 0x6e, 0x03, 0xc2, 0x8b, 0xbc, 0x9e, 0x6c, 0x62, 0xe9, 0xac,
+ 0x23, 0xba, 0xc8, 0x6b, 0x48, 0x26, 0x99, 0x1b, 0x10, 0x4c, 0xa1, 0x36, 0x5f, 0xae, 0xbe, 0xd6,
+ 0xb9, 0x5c, 0x6d, 0xb8, 0xd5, 0x8f, 0x28, 0xee, 0xdb, 0x06, 0x49, 0xd9, 0xfa, 0xd5, 0x47, 0x31,
+ 0xda, 0x75, 0xb0, 0xe4, 0xcf, 0x28, 0xee, 0x97, 0x3b, 0xd9, 0x5a, 0x21, 0x81, 0xb5, 0x77, 0xb5,
+ 0x7a, 0xeb, 0x16, 0x6e, 0x58, 0x45, 0x2a, 0x4d, 0x70, 0xec, 0x44, 0x83, 0x80, 0xab, 0x56, 0x3a,
+ 0xc4, 0xfd, 0x5d, 0x68, 0xe2, 0x48, 0x22, 0x59, 0x91, 0x0d, 0x83, 0x96, 0x4f, 0xca, 0x33, 0x0f,
+ 0x18, 0x08, 0x9e, 0x4f, 0xe8, 0x90, 0x63, 0x42, 0xa6, 0xbd, 0x0c, 0xe5, 0xe5, 0x5f, 0x3d, 0x87,
+ 0x9f, 0x86, 0x01, 0x12, 0x2e, 0x99, 0x53, 0x89, 0xe6, 0xf0, 0x03, 0x2e, 0x45, 0xae, 0xa7, 0xdf,
+ 0x85, 0x9b, 0xad, 0xef, 0x40, 0x2b, 0x11, 0xe6, 0xb3, 0x71, 0xbc, 0x62, 0xd7, 0x09, 0xe9, 0x5e,
+ 0xb0, 0xa3, 0x76, 0x41, 0x7b, 0xd9, 0x52, 0xfb, 0xac, 0xbd, 0x74, 0x03, 0x49, 0xbe, 0xaf, 0x83,
+ 0xae, 0x74, 0xd0, 0x95, 0x0a, 0x6a, 0xab, 0x2f, 0x29, 0xd7, 0x97, 0xe8, 0xf5, 0x25, 0x35, 0xf5,
+ 0x25, 0x7a, 0x7d, 0x49, 0xa9, 0xbe, 0x07, 0xde, 0xba, 0x14, 0x1d, 0xee, 0xe9, 0x9d, 0xe4, 0x8b,
+ 0x31, 0x7a, 0xac, 0x6b, 0x96, 0x6c, 0x00, 0xf5, 0x7a, 0x92, 0x8a, 0x7a, 0x76, 0xd0, 0xc8, 0xd6,
+ 0x5c, 0x8d, 0xac, 0xde, 0x88, 0xe4, 0xe2, 0xf1, 0x2a, 0x1e, 0xa6, 0x7b, 0x5d, 0xf2, 0x0e, 0xb7,
+ 0x6a, 0x1d, 0x7a, 0xb9, 0xd9, 0xe7, 0x5a, 0xb1, 0x8f, 0x83, 0x87, 0x5a, 0x67, 0xed, 0x22, 0xd0,
+ 0x6b, 0x7b, 0xe3, 0x9b, 0x13, 0xd8, 0xdb, 0xdd, 0xe0, 0xad, 0xb8, 0xbf, 0xfb, 0x95, 0x6e, 0xef,
+ 0x56, 0xdc, 0xdd, 0x7d, 0xb4, 0x9b, 0xbb, 0x7b, 0x36, 0x2a, 0x6c, 0x77, 0x81, 0x41, 0x10, 0x31,
+ 0xd7, 0xbf, 0x77, 0xc0, 0xa0, 0x07, 0x4f, 0xc3, 0x92, 0x38, 0x9a, 0x0d, 0x47, 0xb3, 0xe1, 0x68,
+ 0x36, 0x1c, 0xcd, 0x86, 0x0a, 0xb3, 0xe1, 0x9f, 0x79, 0x3e, 0x79, 0xb8, 0xe9, 0xf0, 0xdc, 0xad,
+ 0x82, 0x83, 0x04, 0x6c, 0xa9, 0x35, 0x1d, 0xc4, 0x38, 0x1d, 0xc0, 0x7c, 0x28, 0xd5, 0xb5, 0xde,
+ 0x1a, 0xd0, 0x74, 0xfe, 0xc7, 0x88, 0x88, 0x72, 0x54, 0xfd, 0x8f, 0xaa, 0xff, 0x51, 0xf5, 0x7f,
+ 0x5e, 0xaa, 0xff, 0x86, 0x8a, 0xfa, 0x61, 0x54, 0xf4, 0x53, 0x92, 0x3a, 0x86, 0xc3, 0xe8, 0x7a,
+ 0x20, 0x35, 0x3e, 0xa5, 0x44, 0xb6, 0xba, 0x81, 0xab, 0xc9, 0x12, 0xcc, 0x95, 0x37, 0x00, 0x4c,
+ 0x49, 0x78, 0x9b, 0xce, 0x8b, 0x8c, 0x48, 0xd8, 0xfe, 0x08, 0xce, 0xd1, 0xa4, 0xd3, 0x22, 0xac,
+ 0x95, 0x48, 0xeb, 0x0f, 0x4f, 0x82, 0x5e, 0x38, 0x25, 0xe5, 0x09, 0xca, 0x54, 0xdb, 0x28, 0x3e,
+ 0x23, 0x42, 0x93, 0x6f, 0xfd, 0xd8, 0x6f, 0xd1, 0xc6, 0x95, 0xd7, 0x64, 0x49, 0x4e, 0xf5, 0x3d,
+ 0x21, 0xc8, 0xad, 0xbf, 0x89, 0xc0, 0x21, 0x6a, 0x44, 0xdb, 0x75, 0xcd, 0x45, 0xa5, 0xeb, 0x35,
+ 0x17, 0x95, 0xae, 0x37, 0xb8, 0x0c, 0x71, 0xbd, 0xc9, 0x8d, 0x99, 0x87, 0x1e, 0xc0, 0xd9, 0xfc,
+ 0x24, 0xc5, 0x26, 0x3a, 0x36, 0x8e, 0x87, 0xda, 0x0a, 0x48, 0xb0, 0x45, 0xe9, 0x20, 0xc9, 0x3e,
+ 0x1f, 0x24, 0xeb, 0xbd, 0x21, 0x5a, 0x70, 0x8b, 0x61, 0x50, 0x0b, 0x5f, 0x57, 0xdd, 0x59, 0xba,
+ 0xe6, 0x77, 0x96, 0xae, 0xeb, 0xee, 0x2c, 0xf1, 0xe2, 0xdb, 0x8c, 0x40, 0xf9, 0x64, 0xc7, 0x23,
+ 0x1e, 0xe2, 0xc1, 0x59, 0x4b, 0x67, 0x48, 0xa4, 0x4c, 0x15, 0x22, 0xb5, 0xef, 0x43, 0xec, 0x28,
+ 0xb8, 0xc1, 0x09, 0xae, 0x03, 0xf8, 0x70, 0x43, 0x46, 0x3f, 0x4f, 0xa4, 0x9f, 0xdc, 0xed, 0x40,
+ 0xc3, 0x63, 0x52, 0x1c, 0xed, 0x25, 0x5e, 0x6f, 0x5a, 0xab, 0x04, 0x16, 0x78, 0x90, 0xb0, 0xd3,
+ 0xf8, 0x91, 0x75, 0x4d, 0xe3, 0x07, 0x1c, 0x1f, 0x50, 0x0d, 0x95, 0xfb, 0x9d, 0xb7, 0x18, 0xd4,
+ 0x16, 0x7f, 0xb6, 0xe7, 0x5e, 0xa7, 0xdd, 0xeb, 0xf6, 0x5e, 0x34, 0xe9, 0xe7, 0x88, 0x7c, 0xbe,
+ 0xec, 0x9e, 0xf7, 0xd8, 0x67, 0x42, 0x3e, 0x3b, 0x2f, 0x7b, 0xbd, 0x90, 0x4d, 0x6f, 0xfd, 0x48,
+ 0xa3, 0x38, 0x25, 0x4b, 0x19, 0x75, 0x92, 0x80, 0x8c, 0x41, 0x99, 0x24, 0xe0, 0x4e, 0x7d, 0xf9,
+ 0x0f, 0x82, 0x22, 0x9a, 0x66, 0xa4, 0x02, 0x0b, 0x7f, 0xcc, 0x1c, 0x8e, 0x8e, 0xe6, 0x53, 0xd2,
+ 0x77, 0xf4, 0xb8, 0x2f, 0x5c, 0xe6, 0xa1, 0xed, 0x0f, 0xba, 0x2f, 0x4e, 0x5f, 0x9d, 0x41, 0x28,
+ 0x59, 0x21, 0x11, 0x5d, 0x59, 0xbd, 0x5a, 0x12, 0x64, 0x3a, 0x48, 0xe2, 0x96, 0x29, 0x1d, 0x5d,
+ 0x88, 0x8f, 0x4a, 0xb3, 0x64, 0x27, 0x34, 0xcb, 0x50, 0x80, 0xd1, 0x57, 0x30, 0xd2, 0xa0, 0x11,
+ 0x6c, 0x18, 0x56, 0x8d, 0xef, 0x22, 0xb8, 0xf9, 0xd1, 0xf8, 0xb7, 0x02, 0xe1, 0x45, 0xbc, 0xc6,
+ 0x92, 0xc0, 0x75, 0x9b, 0x8e, 0x25, 0xd5, 0xa3, 0xcd, 0x73, 0x8a, 0x26, 0xb2, 0x81, 0x37, 0xcb,
+ 0xef, 0x1c, 0x3a, 0x5e, 0x41, 0xf7, 0xbc, 0xc3, 0xce, 0x89, 0xfa, 0xd0, 0x12, 0x32, 0x18, 0xe4,
+ 0xe3, 0xf5, 0x2b, 0xdf, 0xde, 0x22, 0xa0, 0x15, 0x83, 0xf7, 0xf6, 0xd9, 0x60, 0x36, 0x59, 0xcf,
+ 0xf1, 0xb1, 0x55, 0xa9, 0x54, 0xd7, 0x9a, 0xf6, 0x9c, 0xa1, 0x87, 0xba, 0x4e, 0x55, 0xec, 0x2c,
+ 0xdb, 0x65, 0x03, 0xcb, 0x10, 0x8f, 0x74, 0xc4, 0xa3, 0x6a, 0xc4, 0xa3, 0x7a, 0xc4, 0x23, 0x03,
+ 0x71, 0xa2, 0x23, 0x4e, 0xaa, 0x11, 0x27, 0xf5, 0x88, 0x13, 0x1d, 0xb1, 0xa7, 0xe8, 0x8e, 0xfa,
+ 0xd1, 0x0e, 0xb9, 0x54, 0xd5, 0xdc, 0xc7, 0x55, 0x16, 0xb3, 0x27, 0xeb, 0xa1, 0x33, 0x56, 0x30,
+ 0x76, 0x0c, 0xd7, 0xea, 0xbf, 0x7b, 0xa0, 0x62, 0xb0, 0x47, 0x95, 0xe5, 0x01, 0x3a, 0xc7, 0xe6,
+ 0x7b, 0x39, 0x30, 0xe4, 0xac, 0xde, 0xd1, 0x23, 0x28, 0x28, 0x5a, 0x68, 0x3c, 0x58, 0xf6, 0x6c,
+ 0xd1, 0x41, 0x2c, 0x7a, 0x4b, 0x09, 0xeb, 0xe3, 0xaa, 0x2d, 0x2a, 0x59, 0xd7, 0xfa, 0xf5, 0x6a,
+ 0xa5, 0xa8, 0x5d, 0x9f, 0xb1, 0xe0, 0x3e, 0xba, 0x46, 0x9f, 0xa0, 0x6b, 0x14, 0xb9, 0x74, 0x2b,
+ 0x55, 0xcd, 0xe0, 0xc9, 0xed, 0x34, 0xb5, 0xed, 0xb5, 0xb3, 0x12, 0xa7, 0x45, 0xbb, 0xf0, 0xd3,
+ 0xd1, 0xf7, 0xfa, 0xed, 0x1c, 0xd9, 0xa8, 0x53, 0x79, 0xd9, 0x7d, 0x94, 0xe1, 0x28, 0x15, 0x37,
+ 0xc0, 0x54, 0x90, 0xe7, 0xef, 0xb4, 0x7d, 0x0c, 0x9d, 0x9f, 0xe6, 0xdc, 0x47, 0x15, 0xba, 0xfd,
+ 0x5a, 0x9b, 0xe0, 0x5e, 0x9c, 0x99, 0x01, 0x97, 0x0f, 0xc7, 0x1d, 0xc1, 0x86, 0x66, 0xe3, 0xaf,
+ 0x7f, 0x6d, 0x70, 0xcd, 0x36, 0x02, 0xc5, 0x56, 0x49, 0xb8, 0x27, 0x10, 0x54, 0xe9, 0x3e, 0xf9,
+ 0x91, 0xae, 0xcb, 0x27, 0x3f, 0xd0, 0xb7, 0x25, 0xd4, 0xf1, 0xf4, 0xef, 0x3d, 0x43, 0x03, 0x39,
+ 0xc8, 0xf9, 0x95, 0xc3, 0x9c, 0x5e, 0x61, 0xb5, 0x70, 0xd5, 0xa1, 0xc2, 0xf8, 0xd9, 0xd0, 0x7a,
+ 0x39, 0x7d, 0x0c, 0xeb, 0x05, 0x2e, 0x40, 0x3e, 0x17, 0x93, 0x85, 0x77, 0x6b, 0x7b, 0x3e, 0x12,
+ 0x56, 0x40, 0x95, 0xf6, 0x8f, 0x98, 0x31, 0x4c, 0x1d, 0x34, 0xc5, 0x67, 0xbf, 0x4e, 0xdd, 0x92,
+ 0x1d, 0xc0, 0xf2, 0xb0, 0x16, 0x65, 0xe8, 0x0c, 0x73, 0xe0, 0xd0, 0x3b, 0x09, 0xbb, 0xe8, 0xa2,
+ 0xfb, 0x55, 0x2a, 0x0f, 0xa5, 0x19, 0x3e, 0xef, 0xad, 0x0b, 0x1c, 0xa8, 0xf5, 0x1a, 0x56, 0xe5,
+ 0x28, 0x6c, 0xa6, 0x2d, 0x1d, 0x37, 0x3c, 0x8e, 0x1b, 0x1e, 0x3b, 0x6f, 0x78, 0xb0, 0xc7, 0x8a,
+ 0x96, 0xec, 0xf9, 0xa0, 0xea, 0x3d, 0x8f, 0xd2, 0xd6, 0x08, 0x2b, 0xe1, 0x19, 0xf2, 0x6a, 0xbf,
+ 0xfe, 0x90, 0x61, 0x36, 0x81, 0x95, 0x22, 0x9f, 0x86, 0x9b, 0x85, 0x72, 0x60, 0xcd, 0x7e, 0x8c,
+ 0x98, 0x38, 0x6a, 0x1c, 0x03, 0x1a, 0xe1, 0x69, 0xb6, 0xc8, 0xc6, 0x04, 0x96, 0x05, 0x55, 0x15,
+ 0x94, 0xb1, 0x9b, 0xd2, 0xa8, 0x07, 0x3b, 0x4a, 0x06, 0x51, 0x1b, 0x41, 0x61, 0x0b, 0xb4, 0x24,
+ 0x7e, 0xf6, 0x55, 0x23, 0x35, 0x78, 0xd5, 0x7d, 0x8d, 0xe1, 0xaa, 0x10, 0x49, 0x93, 0x55, 0x54,
+ 0xf3, 0xe4, 0x90, 0xf1, 0xac, 0x1a, 0x76, 0xe9, 0x36, 0x0b, 0x53, 0x75, 0x37, 0x9b, 0x6b, 0xde,
+ 0x7d, 0x4e, 0x24, 0x45, 0xe9, 0xed, 0x91, 0x78, 0xa9, 0xa5, 0x33, 0xa1, 0x49, 0x0d, 0x43, 0xd6,
+ 0x1f, 0x44, 0x0b, 0x7b, 0x37, 0x4e, 0x6f, 0x41, 0x71, 0x9a, 0x3a, 0x2c, 0xe8, 0x31, 0xe1, 0x53,
+ 0x9f, 0x49, 0xc6, 0x2c, 0x5e, 0xf0, 0xc8, 0x12, 0x18, 0x73, 0xb6, 0xcc, 0xaf, 0x74, 0x92, 0x79,
+ 0x18, 0x27, 0x2d, 0xc4, 0x40, 0x88, 0x71, 0x04, 0xb1, 0xbc, 0xf8, 0xfb, 0x4d, 0xa0, 0x4f, 0x41,
+ 0xb2, 0xcf, 0x2b, 0x76, 0x03, 0xa2, 0x9c, 0x12, 0x4d, 0xa2, 0xf4, 0x70, 0x49, 0xfd, 0xc0, 0xa1,
+ 0x62, 0xaa, 0x90, 0x0a, 0xfd, 0xd3, 0x44, 0xc5, 0xb6, 0xc5, 0x0a, 0x12, 0xcd, 0x5b, 0xfc, 0x5c,
+ 0xa1, 0xa6, 0xc6, 0xb5, 0xbc, 0xa4, 0xb2, 0x6c, 0xa7, 0xaa, 0xc8, 0xa0, 0xb2, 0xc8, 0xda, 0xda,
+ 0x86, 0x9b, 0x50, 0xda, 0xd1, 0x8a, 0xa4, 0x96, 0x22, 0x6a, 0xfe, 0xd5, 0x06, 0xd4, 0xe8, 0x18,
+ 0x47, 0x9b, 0x10, 0x61, 0xa7, 0xff, 0xba, 0xa6, 0xb7, 0xec, 0x25, 0xb2, 0x0d, 0xc8, 0xb3, 0x97,
+ 0x4c, 0x97, 0xf1, 0x68, 0x94, 0x62, 0xd0, 0x09, 0x98, 0xdd, 0xc0, 0xae, 0x8d, 0xbf, 0x35, 0x7a,
+ 0x68, 0x2f, 0x75, 0xda, 0x67, 0xc4, 0x58, 0x12, 0x89, 0x67, 0xed, 0x73, 0x4c, 0x3c, 0x3d, 0x27,
+ 0xa9, 0xe4, 0x0f, 0xb3, 0x0a, 0xd3, 0x79, 0x76, 0xcb, 0x8c, 0xc1, 0x41, 0xf3, 0x8a, 0xfc, 0x97,
+ 0xb9, 0x2d, 0x27, 0x6e, 0x0e, 0xc9, 0x7f, 0x23, 0xd7, 0x77, 0x46, 0xcd, 0x6b, 0xf2, 0x1f, 0x4d,
+ 0x4b, 0xc8, 0x7f, 0x03, 0xd7, 0x0d, 0x1a, 0xa0, 0x06, 0x93, 0x3a, 0x7c, 0x87, 0x22, 0x6f, 0x89,
+ 0xa9, 0x02, 0x41, 0x16, 0x24, 0x41, 0xcd, 0xee, 0xeb, 0x76, 0xef, 0xfc, 0x45, 0xaf, 0x45, 0xc1,
+ 0xcc, 0x38, 0x54, 0xa8, 0xfa, 0xa2, 0x20, 0x42, 0x22, 0xda, 0x4b, 0x38, 0x07, 0x0f, 0x81, 0x4e,
+ 0x7c, 0xfa, 0xbd, 0x12, 0xdf, 0x3c, 0xf8, 0x98, 0xb4, 0x1e, 0xf6, 0x20, 0x92, 0xbf, 0x92, 0xe4,
+ 0xa5, 0x82, 0xd2, 0x26, 0x27, 0xeb, 0x83, 0x8f, 0xad, 0x97, 0x76, 0x63, 0x52, 0x61, 0x3c, 0x55,
+ 0xae, 0x07, 0xf0, 0x74, 0xd4, 0xb4, 0x4c, 0x8f, 0xf9, 0x75, 0x3c, 0xcc, 0xef, 0xcc, 0x54, 0x10,
+ 0xc0, 0x56, 0xf0, 0x78, 0x00, 0xe1, 0x50, 0x64, 0x48, 0xc7, 0x8f, 0x17, 0x8d, 0xd3, 0x76, 0xf7,
+ 0xac, 0x7b, 0xfe, 0xba, 0xf7, 0xe2, 0xfc, 0xf4, 0xfc, 0xd5, 0xeb, 0x97, 0xaf, 0x4f, 0x4f, 0x2c,
+ 0xd1, 0x75, 0xc0, 0x1a, 0xad, 0x0c, 0x6c, 0xa6, 0x72, 0xa4, 0x43, 0x5f, 0xc6, 0x23, 0x56, 0x12,
+ 0x04, 0x37, 0x74, 0xd5, 0xe8, 0x86, 0xb8, 0x7a, 0xfc, 0x4c, 0xa3, 0xd1, 0x0c, 0x88, 0xe1, 0x0b,
+ 0xb6, 0x5a, 0x3c, 0x5d, 0x38, 0x8e, 0x6c, 0xf2, 0x9f, 0x9d, 0xcf, 0x2d, 0xe5, 0xab, 0xfb, 0xd9,
+ 0xf5, 0xd0, 0xf0, 0x63, 0x21, 0xde, 0xdc, 0xa6, 0x9e, 0x29, 0x30, 0x8f, 0xf3, 0x59, 0x1a, 0x11,
+ 0x61, 0x3b, 0x25, 0xd0, 0xbd, 0x73, 0x8f, 0x1d, 0x93, 0x46, 0x8a, 0xdc, 0x40, 0xa9, 0x57, 0x9c,
+ 0x91, 0xc6, 0xb8, 0x2f, 0x11, 0xe3, 0x5f, 0x6a, 0x72, 0x92, 0x49, 0x86, 0x18, 0x18, 0x13, 0xfb,
+ 0x2d, 0x96, 0xeb, 0x92, 0x69, 0xf7, 0xf1, 0x02, 0xef, 0x7a, 0xf0, 0x3c, 0xe1, 0xda, 0x00, 0xa7,
+ 0xc4, 0x0f, 0x6a, 0x1b, 0x85, 0xe9, 0x18, 0xb1, 0xf1, 0x12, 0xee, 0xab, 0xf8, 0x3e, 0x9b, 0xdc,
+ 0x60, 0xe0, 0x5f, 0x9a, 0xbe, 0x6a, 0x7e, 0xbc, 0x08, 0x15, 0x3d, 0xbc, 0xdb, 0x7e, 0xf5, 0xf2,
+ 0xbc, 0x25, 0x83, 0xfc, 0x75, 0xdb, 0x2f, 0xcf, 0x59, 0x3e, 0x99, 0xa4, 0xf4, 0x61, 0x4f, 0x08,
+ 0x26, 0x24, 0x4a, 0xd1, 0x28, 0x8f, 0x97, 0xd8, 0x74, 0x51, 0x0c, 0x9b, 0x02, 0xf2, 0x82, 0x8c,
+ 0x03, 0x99, 0xec, 0xe8, 0x64, 0xc5, 0xde, 0xa1, 0x23, 0x11, 0xc8, 0x54, 0x8e, 0x94, 0x66, 0xb8,
+ 0x1e, 0xff, 0x86, 0x88, 0xb9, 0x50, 0x80, 0x93, 0x8d, 0x0c, 0x83, 0xe3, 0xa5, 0x54, 0xc8, 0xf7,
+ 0x50, 0x31, 0x93, 0xed, 0xb5, 0xe2, 0x40, 0xd1, 0x14, 0x08, 0x0f, 0x46, 0x7f, 0x31, 0x43, 0x5c,
+ 0x36, 0xab, 0x57, 0xf6, 0x45, 0x00, 0x0b, 0xd3, 0x70, 0x2a, 0xa0, 0x7c, 0x3a, 0x74, 0x6c, 0x9a,
+ 0xac, 0xbf, 0xdc, 0xe0, 0xe3, 0x05, 0xca, 0x12, 0xc9, 0x4d, 0x34, 0x40, 0x33, 0x14, 0x62, 0x35,
+ 0xd3, 0x27, 0xb4, 0xe8, 0x4c, 0xf0, 0x15, 0xe6, 0xf7, 0x11, 0x88, 0xa8, 0x2d, 0x84, 0x93, 0x55,
+ 0xe2, 0xeb, 0x89, 0xd2, 0xe4, 0x90, 0xda, 0x42, 0xca, 0x8a, 0x4a, 0xc5, 0xed, 0xd8, 0x6d, 0x2a,
+ 0x9f, 0x0f, 0x13, 0x6e, 0x62, 0x4a, 0x22, 0xa5, 0x18, 0xc2, 0xf8, 0xfc, 0xd5, 0xcb, 0xd3, 0x4e,
+ 0xf7, 0xc5, 0x89, 0x4d, 0xc2, 0x31, 0xbf, 0x64, 0x79, 0xf3, 0x09, 0x63, 0xdc, 0xd7, 0x2b, 0xaf,
+ 0xe5, 0xa8, 0xb0, 0xea, 0xe3, 0x58, 0x4c, 0xda, 0x98, 0xaf, 0xf9, 0x6d, 0xfa, 0xc6, 0x96, 0x21,
+ 0xdf, 0x18, 0x99, 0x46, 0x2a, 0x46, 0xbf, 0xe9, 0x95, 0x62, 0x23, 0x8d, 0xe2, 0xc9, 0x24, 0xe6,
+ 0xc1, 0x8e, 0x2c, 0xa1, 0x3e, 0x41, 0xb7, 0x9f, 0xa6, 0x8b, 0xfc, 0x2a, 0x9e, 0x7f, 0x73, 0x81,
+ 0x63, 0x8e, 0x8f, 0x59, 0xed, 0x70, 0xae, 0x74, 0x14, 0xcf, 0x6a, 0x1e, 0x83, 0xe2, 0xb9, 0x75,
+ 0x71, 0x91, 0x39, 0xcc, 0xba, 0xc8, 0xc8, 0x2a, 0x5c, 0x5d, 0x97, 0xe1, 0xdb, 0xab, 0x15, 0x4d,
+ 0xc2, 0xbc, 0xea, 0x0e, 0xc3, 0xec, 0xfa, 0x4e, 0x11, 0x20, 0x35, 0x24, 0xd4, 0x74, 0xc8, 0xda,
+ 0xde, 0xd8, 0xa8, 0x2b, 0xf4, 0x7e, 0x78, 0x7a, 0x41, 0x86, 0x9e, 0xfb, 0x33, 0x4d, 0x82, 0xe9,
+ 0xf5, 0x81, 0xe1, 0xc9, 0x16, 0x42, 0x78, 0x96, 0xaf, 0xce, 0x89, 0x8a, 0xa0, 0xd5, 0x12, 0xcd,
+ 0x96, 0x3c, 0xaf, 0xb6, 0x88, 0xbe, 0x87, 0x6c, 0xe9, 0x11, 0xcc, 0xf0, 0xe5, 0x5c, 0xb0, 0xf6,
+ 0x07, 0x2b, 0xbd, 0x0d, 0xbb, 0xeb, 0x6d, 0xa8, 0xea, 0x07, 0xde, 0x09, 0xf5, 0x3d, 0xc0, 0x9b,
+ 0xaf, 0xb7, 0x9d, 0xe6, 0xbd, 0x79, 0xff, 0xfb, 0xc5, 0x9b, 0x5f, 0x2f, 0xde, 0x5c, 0x5e, 0xbc,
+ 0xff, 0xa5, 0xfa, 0x79, 0x1a, 0x50, 0xf2, 0xf5, 0x00, 0x9c, 0x51, 0xcc, 0x1f, 0x55, 0x68, 0x75,
+ 0x7b, 0xaf, 0x48, 0x36, 0xd3, 0x9e, 0xfa, 0x62, 0x8b, 0x20, 0xa2, 0xa1, 0xbf, 0xe9, 0x93, 0x09,
+ 0x18, 0xd6, 0x11, 0xbd, 0x05, 0xca, 0x92, 0x16, 0x89, 0x68, 0xdb, 0x14, 0x08, 0xfc, 0x05, 0x6e,
+ 0x93, 0x7d, 0xdd, 0x79, 0x2f, 0xce, 0x58, 0xc4, 0x70, 0xe9, 0x1f, 0x54, 0xa2, 0x73, 0xb3, 0x85,
+ 0x96, 0xbe, 0xed, 0xa4, 0xcd, 0x7a, 0xb1, 0xd9, 0x29, 0xa1, 0x30, 0xc0, 0x38, 0x58, 0x1e, 0x62,
+ 0x37, 0x8d, 0xff, 0xe0, 0x11, 0xc9, 0x43, 0xbe, 0x72, 0xb3, 0x9c, 0x50, 0xb0, 0x0d, 0xff, 0x81,
+ 0x9d, 0xc0, 0x0c, 0xe7, 0x78, 0x7c, 0x45, 0x33, 0x65, 0x0e, 0x1b, 0x64, 0xc0, 0xe5, 0xb1, 0x71,
+ 0xe6, 0x1a, 0x32, 0xa4, 0x73, 0x2c, 0x4d, 0x87, 0xff, 0x12, 0x3a, 0xb5, 0x3a, 0x04, 0x68, 0xf1,
+ 0x0a, 0x45, 0x31, 0xbf, 0x29, 0xb4, 0xb2, 0xa2, 0x5e, 0xaf, 0x8c, 0xa5, 0x87, 0x6f, 0x3f, 0x80,
+ 0x92, 0xd5, 0x74, 0x24, 0x7d, 0x4c, 0xcf, 0xa7, 0x0f, 0x43, 0xa8, 0x15, 0x19, 0x7d, 0x86, 0x97,
+ 0x2d, 0x69, 0x75, 0x9e, 0x0c, 0x42, 0x49, 0x15, 0x13, 0x43, 0x5c, 0xa2, 0x52, 0xae, 0x8c, 0xb3,
+ 0x91, 0x5f, 0xd0, 0xb0, 0xd4, 0x44, 0xb9, 0xbd, 0x31, 0xea, 0xa0, 0xbd, 0xd2, 0x8b, 0xe8, 0x5f,
+ 0xb3, 0x1a, 0x4f, 0x8c, 0x08, 0x51, 0xde, 0x20, 0x22, 0x74, 0xeb, 0xc6, 0xbf, 0x81, 0xa7, 0xd2,
+ 0x8d, 0xf7, 0xb1, 0x38, 0xff, 0xe9, 0x76, 0x2a, 0xd0, 0x1f, 0x30, 0xc5, 0x8e, 0x6f, 0xff, 0xd5,
+ 0x1d, 0xc7, 0x94, 0xcf, 0x8a, 0xf7, 0xb4, 0x62, 0xd8, 0x7f, 0xa5, 0xca, 0xac, 0x81, 0x1b, 0xf1,
+ 0x81, 0x2a, 0xd2, 0x65, 0x6a, 0x54, 0x4d, 0x7e, 0x5f, 0x95, 0xde, 0x3a, 0xad, 0x2c, 0x65, 0x2b,
+ 0x52, 0xaa, 0xb4, 0x74, 0x05, 0xdf, 0x2b, 0xe9, 0xa8, 0x60, 0x94, 0x2b, 0x2a, 0x64, 0xb4, 0x9e,
+ 0xc0, 0x60, 0x0d, 0x35, 0x21, 0x57, 0x55, 0xa9, 0xa7, 0x85, 0x72, 0x84, 0x8f, 0x2c, 0xac, 0xdd,
+ 0xf1, 0xd9, 0x52, 0x29, 0x36, 0xd4, 0x5f, 0xbb, 0xaa, 0x6c, 0x53, 0x8a, 0xbf, 0xce, 0xdb, 0x55,
+ 0xdf, 0xd0, 0x9b, 0x52, 0x7b, 0x8e, 0x98, 0x77, 0x98, 0x97, 0x8a, 0x6c, 0x17, 0xbe, 0x29, 0x73,
+ 0xb8, 0x1e, 0x67, 0x9e, 0xf6, 0x22, 0x94, 0x24, 0xf4, 0x22, 0x7c, 0x67, 0xa7, 0x59, 0xb5, 0x60,
+ 0x41, 0x31, 0x95, 0x8d, 0xd4, 0x8b, 0xe2, 0xd4, 0x16, 0x9e, 0x90, 0x1e, 0x83, 0x0a, 0x5b, 0x8e,
+ 0xa8, 0xa0, 0x68, 0x21, 0x6a, 0xd7, 0x97, 0x55, 0x62, 0xa8, 0x52, 0x37, 0xc0, 0xf4, 0x0a, 0x83,
+ 0x79, 0x0f, 0xa1, 0xa9, 0x84, 0x65, 0xfc, 0xdb, 0x9b, 0x7f, 0xf4, 0x7f, 0xbd, 0x78, 0xff, 0xae,
+ 0xff, 0xf6, 0xe2, 0xf2, 0xf7, 0x37, 0xef, 0x7f, 0x7a, 0xd7, 0x38, 0xed, 0xbd, 0x7c, 0xf1, 0xb2,
+ 0xdd, 0x79, 0x32, 0xb6, 0xf3, 0xa6, 0x0f, 0x50, 0x57, 0xd8, 0xd8, 0xfb, 0xb7, 0x9d, 0x89, 0x58,
+ 0x1d, 0xcd, 0xd3, 0x45, 0x65, 0xa0, 0x90, 0xa3, 0x95, 0x7a, 0xb4, 0x52, 0x9f, 0xab, 0x95, 0x7a,
+ 0xb4, 0x1b, 0x8f, 0x76, 0xe3, 0x33, 0xb3, 0x1b, 0xb9, 0x38, 0xdf, 0xd4, 0x74, 0x0c, 0x4a, 0x4b,
+ 0xe8, 0xd1, 0x98, 0x3c, 0x1a, 0x93, 0x47, 0x63, 0xf2, 0x9b, 0x32, 0x26, 0x37, 0xde, 0x5b, 0xde,
+ 0xd9, 0xcc, 0x7c, 0xa0, 0xfe, 0xf8, 0x6c, 0x4c, 0xc5, 0xa3, 0xb9, 0x67, 0x31, 0xf7, 0x2a, 0xde,
+ 0x30, 0x63, 0x07, 0x02, 0x80, 0xb3, 0x75, 0x56, 0xa9, 0x7e, 0xcb, 0x6c, 0x3f, 0x56, 0x62, 0xd9,
+ 0x06, 0xe4, 0x39, 0xda, 0xc2, 0xd7, 0xbf, 0xfc, 0xe9, 0xcd, 0xaf, 0xef, 0x40, 0xb4, 0x3f, 0xba,
+ 0x8d, 0xb8, 0x66, 0x07, 0xf5, 0x61, 0x26, 0xe4, 0x43, 0x6c, 0x44, 0xb9, 0x7b, 0xba, 0x83, 0x93,
+ 0xe9, 0x68, 0x1a, 0xee, 0x60, 0x1a, 0x3e, 0x01, 0x4b, 0xec, 0xc9, 0x59, 0xa7, 0x07, 0x36, 0x0d,
+ 0x8f, 0x17, 0x42, 0x9f, 0xd0, 0x85, 0xd0, 0x67, 0x6e, 0x82, 0x7f, 0x5d, 0x93, 0xf7, 0xa9, 0x39,
+ 0x00, 0x0e, 0x65, 0x82, 0xff, 0x27, 0x5c, 0xd1, 0xfd, 0x4a, 0x6e, 0x06, 0x0e, 0xb7, 0xdd, 0x26,
+ 0xb5, 0x45, 0xd1, 0x3a, 0xfa, 0x19, 0x8e, 0x7e, 0x86, 0xa3, 0x9f, 0xe1, 0x50, 0x7e, 0x06, 0x39,
+ 0x5d, 0x63, 0x55, 0xe9, 0x7f, 0x5c, 0xff, 0xc3, 0x36, 0xd7, 0x51, 0x35, 0x59, 0x67, 0xc6, 0x79,
+ 0x59, 0xeb, 0xc8, 0x38, 0x9c, 0x91, 0xf3, 0x5c, 0x6e, 0x83, 0xfe, 0x27, 0x78, 0x67, 0x9e, 0xd0,
+ 0xb5, 0xd2, 0xaf, 0xe8, 0x3d, 0x3a, 0xc6, 0x4d, 0x7e, 0x5e, 0xaf, 0xa9, 0xa8, 0x5d, 0xad, 0xb6,
+ 0x4f, 0x6f, 0x73, 0x7b, 0xe9, 0x89, 0x5e, 0x0b, 0xf4, 0x08, 0xc4, 0x06, 0xe0, 0xca, 0x86, 0x33,
+ 0xb1, 0xe0, 0x24, 0x54, 0x7b, 0xac, 0xbf, 0xeb, 0x30, 0x02, 0xc5, 0xe1, 0x37, 0xe6, 0xb2, 0xa4,
+ 0xd8, 0x96, 0xa4, 0x2b, 0xe9, 0xf5, 0x66, 0xb1, 0x44, 0x04, 0x7a, 0x4f, 0xb7, 0x97, 0x3a, 0x7c,
+ 0x52, 0x0f, 0x9f, 0xe8, 0xf0, 0xab, 0x3e, 0xbe, 0xc4, 0xdc, 0x14, 0x7d, 0x41, 0xf4, 0x20, 0x76,
+ 0x59, 0x4e, 0xd0, 0x87, 0x34, 0x39, 0x46, 0xad, 0xab, 0x66, 0x8f, 0xbd, 0x26, 0xe3, 0x9a, 0x04,
+ 0xad, 0x24, 0xf2, 0x64, 0x17, 0xe4, 0x49, 0x35, 0xf2, 0x44, 0xb2, 0x06, 0xc6, 0x3f, 0xd9, 0xfe,
+ 0xb2, 0x39, 0xf2, 0x10, 0xe9, 0x55, 0x9f, 0xb4, 0xdc, 0x55, 0x70, 0x25, 0x3b, 0x5c, 0x15, 0x67,
+ 0xb8, 0x12, 0x82, 0x2b, 0x71, 0x35, 0x27, 0x33, 0xe0, 0xb2, 0x5e, 0x17, 0x27, 0x95, 0xfa, 0xf6,
+ 0x8c, 0xc4, 0xb5, 0x5f, 0x13, 0xa7, 0x6e, 0xe7, 0x47, 0x7c, 0xd6, 0xe2, 0x09, 0x39, 0x9d, 0x1f,
+ 0xf5, 0x60, 0x12, 0x1b, 0x24, 0xfa, 0x0c, 0x76, 0x5c, 0x52, 0xed, 0x48, 0xaf, 0xf7, 0x57, 0x4a,
+ 0xba, 0xad, 0x54, 0x52, 0x51, 0x2a, 0xd9, 0xc7, 0x21, 0x28, 0x96, 0x0a, 0x35, 0xc4, 0xb6, 0xc4,
+ 0x64, 0xf7, 0x53, 0x95, 0xc7, 0xab, 0x44, 0xc7, 0x43, 0x5a, 0xc7, 0x43, 0x5a, 0x55, 0x9e, 0x78,
+ 0x3e, 0x64, 0x16, 0x37, 0xdd, 0x63, 0x78, 0xe9, 0xd1, 0x57, 0x54, 0x43, 0xa5, 0xcc, 0xaf, 0xec,
+ 0x2b, 0x09, 0x62, 0xe3, 0x2f, 0x25, 0xb7, 0xae, 0x37, 0x75, 0xb0, 0xe3, 0xbd, 0xa8, 0xe3, 0xf9,
+ 0xb6, 0x27, 0x72, 0xbe, 0xed, 0x70, 0xee, 0x74, 0x65, 0x2a, 0xca, 0x9f, 0x96, 0x9a, 0x65, 0xa6,
+ 0xaf, 0xcf, 0x4f, 0x5b, 0x93, 0x55, 0x4c, 0xb6, 0x49, 0x76, 0xf4, 0x7a, 0x1f, 0xbd, 0xde, 0x47,
+ 0xaf, 0xf7, 0xd1, 0xeb, 0xbd, 0x99, 0xd7, 0x1b, 0xd5, 0xff, 0x88, 0xc9, 0x21, 0x31, 0x77, 0x3d,
+ 0xd3, 0x9c, 0x69, 0x2f, 0x03, 0x45, 0x48, 0x89, 0xc1, 0x2b, 0x81, 0xad, 0x9a, 0xc2, 0xd2, 0x71,
+ 0x19, 0xf6, 0x64, 0x2d, 0xf6, 0x64, 0x33, 0xec, 0x89, 0x82, 0x1d, 0x42, 0xa3, 0xed, 0xfb, 0x64,
+ 0xa0, 0x69, 0x8b, 0x2d, 0x86, 0x57, 0xd8, 0xfd, 0xe5, 0xd8, 0x59, 0xc4, 0xa4, 0x7c, 0x62, 0x16,
+ 0xd8, 0xf1, 0x5e, 0x5b, 0xbd, 0x0e, 0x70, 0x38, 0x1d, 0x7c, 0xbf, 0x0a, 0xf4, 0xb7, 0x78, 0x55,
+ 0xcf, 0x8a, 0xa1, 0xe6, 0xdc, 0x43, 0xb9, 0xc7, 0x6b, 0x3b, 0x7c, 0x6b, 0x55, 0xe9, 0x1b, 0x73,
+ 0xc9, 0x12, 0x31, 0x04, 0xb9, 0xfd, 0xd8, 0x1a, 0x5b, 0x0a, 0x85, 0x89, 0xdb, 0x8e, 0x0d, 0xe0,
+ 0xa4, 0x12, 0x38, 0x29, 0x01, 0xa3, 0xf7, 0x50, 0xd4, 0xe2, 0x0b, 0x14, 0x3e, 0x4a, 0x3a, 0x37,
+ 0xa4, 0xbe, 0xc0, 0x48, 0x7b, 0x29, 0xf7, 0xbc, 0x25, 0xe5, 0xa3, 0x2a, 0xcb, 0x69, 0x34, 0x63,
+ 0x5b, 0x0e, 0x43, 0xbb, 0xff, 0x43, 0xae, 0x1b, 0x9d, 0x3b, 0x2d, 0xc6, 0x64, 0xad, 0x99, 0xab,
+ 0xcf, 0x64, 0x08, 0xa9, 0x8f, 0x6b, 0x4f, 0x45, 0x66, 0x72, 0x73, 0x75, 0x95, 0xce, 0xeb, 0x9e,
+ 0xd0, 0xdd, 0x32, 0xa8, 0x5a, 0xc7, 0x92, 0xd6, 0xdd, 0x35, 0xd2, 0x5a, 0x27, 0x72, 0x1c, 0x7b,
+ 0x54, 0xca, 0x16, 0xe8, 0xd1, 0x81, 0x4e, 0x7f, 0xc3, 0x85, 0x98, 0x51, 0xb4, 0x64, 0x37, 0xa2,
+ 0x81, 0xf9, 0xe0, 0xa9, 0x76, 0xb5, 0x03, 0x20, 0x98, 0x98, 0xec, 0x2a, 0xa5, 0x73, 0xd5, 0x0d,
+ 0xec, 0x7e, 0xb9, 0x9f, 0xcc, 0x67, 0x46, 0x4a, 0x8b, 0x6d, 0xa7, 0x3a, 0xab, 0xbb, 0x71, 0x37,
+ 0x95, 0x46, 0x67, 0x0e, 0x11, 0xad, 0x88, 0xd2, 0xb1, 0x80, 0x70, 0xe0, 0x75, 0xd9, 0x20, 0x2e,
+ 0xcb, 0x23, 0x1f, 0x93, 0x6e, 0xc3, 0xe0, 0x7e, 0xe6, 0xcb, 0x2b, 0xd2, 0xa5, 0x39, 0x2d, 0xe6,
+ 0xf1, 0xc2, 0xf6, 0x32, 0x0b, 0x3c, 0x93, 0xb2, 0x98, 0x65, 0xd3, 0xfe, 0x1d, 0x06, 0xfb, 0x35,
+ 0xa2, 0xe5, 0x49, 0xc1, 0xdc, 0x29, 0x4f, 0xcc, 0x0e, 0x0d, 0x11, 0xd7, 0x59, 0x13, 0x8d, 0xb6,
+ 0x4b, 0xc1, 0xba, 0x34, 0x14, 0x38, 0x45, 0xd6, 0x8e, 0xa9, 0x85, 0x40, 0xaa, 0x60, 0x09, 0xf3,
+ 0x51, 0x12, 0xc9, 0x9f, 0x01, 0x07, 0x0b, 0xbf, 0x88, 0x42, 0x5d, 0xb3, 0x50, 0x57, 0x16, 0xea,
+ 0xca, 0x42, 0x5d, 0x28, 0x64, 0x6c, 0x31, 0x50, 0x6c, 0x3e, 0x0b, 0x36, 0xcb, 0x47, 0x9f, 0xbd,
+ 0x00, 0xd5, 0x8e, 0xd5, 0x90, 0xdc, 0xd8, 0x25, 0x02, 0x2f, 0xa0, 0x0d, 0xe1, 0x0b, 0x43, 0x79,
+ 0x43, 0xd8, 0x4f, 0xf2, 0xe1, 0xeb, 0x3d, 0x06, 0xcf, 0x0b, 0xb8, 0x7e, 0x45, 0xde, 0xfd, 0x72,
+ 0x55, 0x99, 0xb7, 0xba, 0x5f, 0x8a, 0x28, 0x77, 0xf1, 0x6d, 0x4a, 0xa4, 0x43, 0x1a, 0xf1, 0xe7,
+ 0x92, 0xf8, 0x1b, 0x58, 0xfc, 0xf1, 0x2b, 0x37, 0x38, 0x85, 0xf0, 0xa3, 0x10, 0x9e, 0xdc, 0x61,
+ 0xb0, 0x2d, 0xf2, 0x05, 0xc1, 0xd8, 0xca, 0xc3, 0x0f, 0x04, 0x43, 0x85, 0x38, 0x7b, 0xbc, 0xd2,
+ 0xf0, 0xd3, 0xe9, 0x43, 0x87, 0x1e, 0x18, 0x0a, 0xac, 0x7b, 0xda, 0x40, 0x93, 0x15, 0xfd, 0xb5,
+ 0x09, 0x2e, 0x47, 0x44, 0x7e, 0x57, 0xe0, 0x81, 0x2a, 0xfc, 0x0d, 0x52, 0x0c, 0x09, 0x8b, 0x92,
+ 0x82, 0x46, 0x7f, 0xe3, 0x44, 0xfa, 0xa2, 0x1a, 0x1f, 0xdb, 0xce, 0x86, 0xcf, 0xe7, 0x01, 0xdb,
+ 0x1e, 0x26, 0x7f, 0x49, 0x2f, 0x2d, 0x0a, 0x36, 0x65, 0x3e, 0x5e, 0x44, 0xa5, 0xd0, 0x89, 0xe5,
+ 0x9d, 0x1b, 0xb0, 0xe4, 0x99, 0x51, 0x6d, 0xdf, 0xd5, 0xd1, 0x52, 0x4f, 0xa1, 0x84, 0x30, 0xa7,
+ 0x74, 0x99, 0xcb, 0x9d, 0x12, 0xc8, 0x98, 0xa6, 0x10, 0x4a, 0xf2, 0x7c, 0x0c, 0x42, 0x66, 0x41,
+ 0xf7, 0xf6, 0x20, 0x6c, 0x67, 0x1f, 0x89, 0x8d, 0x15, 0x49, 0x6f, 0x00, 0x5d, 0xa5, 0x31, 0xca,
+ 0xd2, 0x12, 0x9c, 0xfe, 0x5c, 0x20, 0xc2, 0xd6, 0x65, 0x56, 0x64, 0x0d, 0xe2, 0x09, 0xe1, 0x41,
+ 0x30, 0xf9, 0x20, 0x84, 0x1e, 0x91, 0xcc, 0xfc, 0x11, 0x86, 0x0a, 0xf8, 0x59, 0x56, 0x0c, 0xae,
+ 0x4d, 0x5a, 0xe7, 0x79, 0x11, 0x17, 0x69, 0x7f, 0xb1, 0x9a, 0x24, 0xf9, 0xb8, 0xa2, 0x20, 0x0d,
+ 0x18, 0x68, 0x98, 0x40, 0x9a, 0x10, 0x1f, 0x5c, 0x6b, 0xf1, 0x34, 0x2b, 0x36, 0xd0, 0x68, 0xea,
+ 0x38, 0x4e, 0x88, 0x06, 0x34, 0x1b, 0xc7, 0xd3, 0xb4, 0x02, 0x82, 0x06, 0xb4, 0x36, 0xf2, 0x64,
+ 0xdf, 0x82, 0x70, 0x33, 0x93, 0xb1, 0x6d, 0xd4, 0x64, 0x9d, 0xc4, 0xb3, 0xaa, 0x13, 0x54, 0x65,
+ 0xb3, 0xa9, 0x64, 0x34, 0x69, 0x83, 0xff, 0x0d, 0xec, 0xc5, 0x3c, 0x91, 0x07, 0xd4, 0xa5, 0x12,
+ 0x13, 0xa9, 0x53, 0x52, 0x9c, 0x6d, 0xe1, 0x1e, 0x5a, 0x3d, 0x97, 0x9f, 0x8c, 0x41, 0xb5, 0x43,
+ 0x3a, 0x13, 0x79, 0x22, 0x8c, 0x19, 0x4f, 0x25, 0xa0, 0x2a, 0x53, 0x2e, 0xd2, 0xd1, 0x04, 0xe2,
+ 0x46, 0x12, 0xb6, 0x1b, 0xa7, 0x51, 0xcb, 0x98, 0xd4, 0x7f, 0xf6, 0x3e, 0x73, 0x2d, 0x15, 0x86,
+ 0x1d, 0x56, 0xaf, 0xef, 0x2a, 0xe6, 0x2e, 0xbc, 0x70, 0xf2, 0x5d, 0xf5, 0x94, 0x25, 0x9d, 0x8a,
+ 0x54, 0x80, 0x0c, 0xa4, 0x04, 0xfd, 0xd9, 0xf9, 0xec, 0xb3, 0x5f, 0xdd, 0xcf, 0x3e, 0x9f, 0xbd,
+ 0x6e, 0xd0, 0x3b, 0x7f, 0x01, 0x91, 0x69, 0x1b, 0xd0, 0x67, 0x0d, 0xa8, 0xf1, 0x61, 0x15, 0x8a,
+ 0xca, 0xca, 0x88, 0xeb, 0x9a, 0xb2, 0x1e, 0x31, 0x13, 0x29, 0x0c, 0x9f, 0x9e, 0xa6, 0xfb, 0xd3,
+ 0x3e, 0xe6, 0xd9, 0xb4, 0x58, 0x17, 0x19, 0x9d, 0x05, 0xb5, 0x16, 0xa2, 0x88, 0xc8, 0x9f, 0xeb,
+ 0x5c, 0x8a, 0xa2, 0x48, 0xc7, 0xd6, 0xd6, 0x47, 0x91, 0x43, 0x51, 0xd1, 0x12, 0x99, 0xd3, 0xb8,
+ 0xf1, 0xe3, 0x49, 0x25, 0xe2, 0xa0, 0x46, 0xfe, 0x35, 0x7e, 0x38, 0xa9, 0xc9, 0x0d, 0x2a, 0x71,
+ 0x6a, 0xb4, 0x29, 0x9e, 0x2f, 0x46, 0x1e, 0xb5, 0xc9, 0xc0, 0x48, 0x01, 0x37, 0xa7, 0x4e, 0x3b,
+ 0xda, 0x5d, 0xe0, 0xa4, 0x0e, 0xa1, 0x27, 0xbd, 0xa8, 0x54, 0x9a, 0x9f, 0xc4, 0x22, 0x5a, 0x00,
+ 0x3f, 0x71, 0xc5, 0x44, 0x59, 0xe3, 0x47, 0xe4, 0xd2, 0xa0, 0x77, 0x86, 0x9e, 0x58, 0x1c, 0x09,
+ 0x8d, 0xd5, 0x51, 0x34, 0x53, 0x41, 0x0d, 0x6a, 0x3c, 0xa9, 0x29, 0xa4, 0xfc, 0xa5, 0xc9, 0x6e,
+ 0xae, 0x28, 0xd2, 0xa9, 0xf5, 0x71, 0xed, 0x20, 0xd2, 0x30, 0xda, 0x5d, 0x8c, 0xee, 0x2d, 0x5e,
+ 0x04, 0x8a, 0xcd, 0xf1, 0x5a, 0xae, 0x82, 0xd2, 0x08, 0x22, 0x64, 0x12, 0xd9, 0x2a, 0x02, 0x78,
+ 0x6b, 0xfa, 0x5d, 0x68, 0x36, 0x03, 0x43, 0x01, 0x3b, 0x70, 0x5a, 0x0b, 0x8f, 0x84, 0xe9, 0xab,
+ 0x8c, 0x0f, 0x07, 0xbe, 0xe0, 0x54, 0x59, 0xf8, 0x45, 0xed, 0x0a, 0x9c, 0xed, 0x84, 0x57, 0xa7,
+ 0x11, 0x46, 0x9c, 0x55, 0x65, 0x40, 0xd3, 0xc0, 0xaf, 0xf3, 0x27, 0x2d, 0x38, 0x20, 0xd2, 0x09,
+ 0xe3, 0xec, 0xd6, 0x17, 0x04, 0x27, 0x73, 0x83, 0x7f, 0xb2, 0x8e, 0x8b, 0xd0, 0xf3, 0x2c, 0xd0,
+ 0xf8, 0xe8, 0xcb, 0x17, 0xe4, 0xf8, 0xe6, 0xaf, 0x01, 0x0b, 0xda, 0xac, 0x7a, 0xa7, 0x41, 0x36,
+ 0xda, 0x56, 0x3f, 0x31, 0x26, 0x2a, 0x24, 0xe9, 0x48, 0x2d, 0xfe, 0x8f, 0x62, 0xc4, 0xa9, 0xab,
+ 0x23, 0xf3, 0x58, 0x1b, 0x25, 0x03, 0x3d, 0xe1, 0xae, 0x69, 0x34, 0xc6, 0x13, 0x1b, 0x66, 0xc1,
+ 0x29, 0xec, 0x7e, 0x08, 0xa6, 0x54, 0xe2, 0x88, 0xdf, 0x32, 0x91, 0x4c, 0xfe, 0x0d, 0x94, 0x05,
+ 0x14, 0x86, 0x5e, 0x5d, 0x22, 0xf9, 0x5a, 0xc2, 0x3e, 0x1d, 0x5d, 0x7b, 0x12, 0x51, 0xe6, 0xa5,
+ 0x8e, 0x10, 0xa9, 0xf9, 0x44, 0x82, 0xa2, 0x35, 0x01, 0xf1, 0x94, 0x35, 0x4d, 0xa2, 0xf1, 0x43,
+ 0x4b, 0xd7, 0x2c, 0xf4, 0x85, 0x59, 0xbc, 0x96, 0x05, 0x0e, 0x12, 0x88, 0x81, 0xae, 0x21, 0xed,
+ 0x7c, 0x6e, 0x2a, 0x45, 0x5d, 0xb7, 0x2a, 0x5c, 0x38, 0x33, 0x92, 0x1e, 0x5b, 0x2b, 0x38, 0xdc,
+ 0xf9, 0xdc, 0xad, 0x1c, 0x69, 0x0a, 0x10, 0x75, 0x31, 0x31, 0x00, 0xcf, 0x68, 0x9d, 0xa6, 0xa3,
+ 0xab, 0x76, 0x24, 0xfb, 0x4d, 0x3d, 0x41, 0x2e, 0x3d, 0xe4, 0x75, 0x54, 0xf6, 0xf7, 0xa7, 0xec,
+ 0x3f, 0x19, 0x25, 0x7a, 0x9f, 0x56, 0xc4, 0xb6, 0x16, 0x4d, 0x9d, 0xd5, 0x51, 0xaf, 0xec, 0x03,
+ 0x7b, 0x68, 0x9e, 0xa0, 0x53, 0x96, 0xd8, 0xad, 0x9a, 0xd0, 0x57, 0xd9, 0x78, 0x5c, 0x77, 0x46,
+ 0x4d, 0xe6, 0x57, 0x1f, 0x54, 0x93, 0x30, 0xb6, 0xd3, 0x6a, 0x4a, 0x6e, 0xdd, 0x56, 0x8b, 0x0e,
+ 0x56, 0xf7, 0x98, 0x57, 0x3c, 0xce, 0xeb, 0x28, 0x96, 0xf9, 0xd5, 0x14, 0x4b, 0x18, 0x1b, 0xc5,
+ 0x4a, 0x6e, 0x1d, 0xc5, 0x3a, 0xd8, 0x37, 0x7d, 0xac, 0x0d, 0x9b, 0x52, 0x73, 0x42, 0x4a, 0xe6,
+ 0x57, 0xbf, 0xbe, 0x2b, 0x40, 0xac, 0x2f, 0xf0, 0xca, 0xdc, 0xda, 0xb7, 0xd6, 0x34, 0xb0, 0x75,
+ 0x04, 0x57, 0x9f, 0x60, 0x14, 0xd9, 0xf5, 0xe4, 0x56, 0x9d, 0x65, 0x94, 0x99, 0x6b, 0x89, 0xd5,
+ 0x76, 0x82, 0x36, 0xb1, 0x53, 0x95, 0x09, 0x27, 0x7f, 0xda, 0xce, 0x75, 0xc9, 0x5c, 0x5f, 0x9f,
+ 0x86, 0xb6, 0x13, 0x5e, 0x2a, 0xaa, 0x9d, 0xa6, 0x92, 0xfc, 0x69, 0xa3, 0x45, 0xe6, 0xfa, 0xfa,
+ 0x04, 0xb3, 0xd1, 0xa2, 0xa2, 0xda, 0x7e, 0x92, 0x1c, 0xfc, 0x40, 0x98, 0xc2, 0xfb, 0xf2, 0xa7,
+ 0xed, 0xfd, 0x5f, 0x91, 0xe9, 0xeb, 0x13, 0xc2, 0xfa, 0x0e, 0xb0, 0x82, 0x69, 0x17, 0xae, 0x16,
+ 0xbf, 0xaa, 0x08, 0x61, 0x47, 0xf1, 0x14, 0x46, 0xaf, 0x24, 0x83, 0xed, 0x59, 0x5a, 0xd8, 0xf5,
+ 0xe8, 0xcf, 0x38, 0xfa, 0x33, 0x8e, 0xfe, 0x8c, 0xa3, 0x3f, 0xe3, 0xe8, 0xcf, 0x78, 0x1e, 0xfe,
+ 0x0c, 0x7e, 0x06, 0x59, 0x9e, 0x88, 0xd3, 0x0e, 0xd0, 0x51, 0x26, 0x78, 0x52, 0x2e, 0x0f, 0xf9,
+ 0xde, 0xd5, 0x7c, 0x96, 0x8f, 0x63, 0x68, 0xfd, 0x03, 0x7c, 0x20, 0xcc, 0x00, 0xa2, 0xe7, 0xdf,
+ 0x48, 0xdb, 0xda, 0x4b, 0xb8, 0xda, 0x85, 0x37, 0xd4, 0xa8, 0x15, 0x44, 0x37, 0x0d, 0x95, 0x0e,
+ 0xf2, 0xe9, 0x0b, 0xa3, 0x55, 0xf5, 0xa3, 0x5b, 0x85, 0xdf, 0xc1, 0xba, 0x7c, 0xfb, 0x73, 0xff,
+ 0xe3, 0x3f, 0x1a, 0xaf, 0xda, 0x9d, 0x93, 0xb2, 0xa9, 0x09, 0xab, 0x6b, 0x58, 0xe7, 0x80, 0xb1,
+ 0x1b, 0x83, 0xea, 0xd1, 0x98, 0x6d, 0xcf, 0xe7, 0x19, 0x76, 0xee, 0x63, 0x9a, 0x82, 0x87, 0x35,
+ 0xe3, 0x0e, 0x68, 0x82, 0x1d, 0xca, 0x8a, 0x3a, 0xa8, 0x09, 0xb4, 0x4f, 0x0b, 0xa6, 0x96, 0x4d,
+ 0xb6, 0x33, 0x40, 0xea, 0xed, 0x8f, 0xad, 0xec, 0x87, 0x7d, 0x9c, 0xb7, 0x53, 0x46, 0x6f, 0x3b,
+ 0x45, 0xbe, 0x6e, 0x68, 0x36, 0xd6, 0xc5, 0x29, 0xcc, 0xbb, 0xb7, 0xbf, 0xbc, 0xeb, 0xff, 0xf2,
+ 0xe6, 0xb7, 0xdf, 0xde, 0x10, 0x8d, 0xa1, 0xdb, 0x39, 0x0f, 0xec, 0xc1, 0xfb, 0xb8, 0x18, 0x67,
+ 0xb3, 0x1c, 0xb4, 0xef, 0xb2, 0xe8, 0x67, 0xb3, 0x5d, 0xdc, 0x9e, 0x47, 0xa5, 0x91, 0x27, 0xae,
+ 0x54, 0x09, 0xce, 0x3b, 0x8a, 0x65, 0x4a, 0x45, 0x7c, 0x43, 0xc5, 0x47, 0x46, 0x91, 0xa0, 0xa3,
+ 0xa6, 0x0c, 0xbf, 0x2a, 0xed, 0x90, 0xb8, 0x48, 0x36, 0x31, 0x70, 0x04, 0x7e, 0x4f, 0x93, 0x84,
+ 0x6e, 0xa8, 0x9e, 0xb8, 0xbc, 0xb9, 0xba, 0x8a, 0x1c, 0xd4, 0xa7, 0x5b, 0x78, 0x43, 0x82, 0xe9,
+ 0xd6, 0x42, 0x5b, 0x87, 0xbe, 0xe4, 0x07, 0x75, 0x22, 0x85, 0x5d, 0x68, 0x75, 0x8e, 0xe8, 0x6a,
+ 0xaf, 0xdb, 0xee, 0xbe, 0x0e, 0xa8, 0x10, 0x6f, 0x4a, 0x2a, 0xdc, 0x1a, 0x32, 0x68, 0xdd, 0x50,
+ 0xb3, 0x1c, 0xf9, 0x40, 0x2e, 0xbc, 0x0c, 0x59, 0xf8, 0x45, 0x21, 0x17, 0x8f, 0x0b, 0xda, 0x3c,
+ 0xca, 0xe0, 0x4f, 0x6e, 0xc7, 0xe5, 0x0e, 0xa1, 0x75, 0x0d, 0x23, 0xfc, 0xf0, 0xd4, 0xf5, 0x40,
+ 0xd3, 0x85, 0xd0, 0x9b, 0xad, 0x1c, 0x30, 0x04, 0xd2, 0x5a, 0x2a, 0x02, 0x1f, 0x52, 0x9a, 0x5a,
+ 0xca, 0x86, 0xa7, 0x09, 0x3d, 0x7d, 0xc5, 0x7b, 0x90, 0xb7, 0xfb, 0xff, 0x01, 0x20, 0xfc, 0x89,
+ 0x13
};
const char* shaderSource() {
diff --git a/src/mbgl/programs/gl/symbol_icon.cpp b/src/mbgl/programs/gl/symbol_icon.cpp
index b7f54b590b..49c7654cf8 100644
--- a/src/mbgl/programs/gl/symbol_icon.cpp
+++ b/src/mbgl/programs/gl/symbol_icon.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<SymbolIconProgram> {
static constexpr const char* name = "symbol_icon";
static constexpr const uint8_t hash[8] = { 0xf3, 0x81, 0x62, 0xe8, 0x24, 0x49, 0xc6, 0x8f };
- static constexpr const auto vertexOffset = 50235;
- static constexpr const auto fragmentOffset = 52883;
+ static constexpr const auto vertexOffset = 50247;
+ static constexpr const auto fragmentOffset = 52895;
};
constexpr const char* ShaderSource<SymbolIconProgram>::name;
diff --git a/src/mbgl/programs/gl/symbol_sdf_icon.cpp b/src/mbgl/programs/gl/symbol_sdf_icon.cpp
index 76228062c6..661fc7d298 100644
--- a/src/mbgl/programs/gl/symbol_sdf_icon.cpp
+++ b/src/mbgl/programs/gl/symbol_sdf_icon.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<SymbolSDFIconProgram> {
static constexpr const char* name = "symbol_sdf_icon";
static constexpr const uint8_t hash[8] = { 0x4b, 0x0b, 0x5f, 0x6b, 0xa9, 0xec, 0x84, 0x19 };
- static constexpr const auto vertexOffset = 53288;
- static constexpr const auto fragmentOffset = 57322;
+ static constexpr const auto vertexOffset = 53300;
+ static constexpr const auto fragmentOffset = 57334;
};
constexpr const char* ShaderSource<SymbolSDFIconProgram>::name;
diff --git a/src/mbgl/programs/gl/symbol_sdf_text.cpp b/src/mbgl/programs/gl/symbol_sdf_text.cpp
index 31acda6f55..7eb0dbabed 100644
--- a/src/mbgl/programs/gl/symbol_sdf_text.cpp
+++ b/src/mbgl/programs/gl/symbol_sdf_text.cpp
@@ -16,8 +16,8 @@ template <>
struct ShaderSource<SymbolSDFTextProgram> {
static constexpr const char* name = "symbol_sdf_text";
static constexpr const uint8_t hash[8] = { 0x4b, 0x0b, 0x5f, 0x6b, 0xa9, 0xec, 0x84, 0x19 };
- static constexpr const auto vertexOffset = 53288;
- static constexpr const auto fragmentOffset = 57322;
+ static constexpr const auto vertexOffset = 53300;
+ static constexpr const auto fragmentOffset = 57334;
};
constexpr const char* ShaderSource<SymbolSDFTextProgram>::name;
diff --git a/src/mbgl/programs/hillshade_prepare_program.hpp b/src/mbgl/programs/hillshade_prepare_program.hpp
index 2d76145bc3..c85b5bc609 100644
--- a/src/mbgl/programs/hillshade_prepare_program.hpp
+++ b/src/mbgl/programs/hillshade_prepare_program.hpp
@@ -11,6 +11,7 @@ namespace mbgl {
namespace uniforms {
MBGL_DEFINE_UNIFORM_VECTOR(uint16_t, 2, dimension);
MBGL_DEFINE_UNIFORM_SCALAR(float, maxzoom);
+MBGL_DEFINE_UNIFORM_VECTOR(float, 4, unpack);
} // namespace uniforms
class HillshadePrepareProgram : public Program<
@@ -23,7 +24,8 @@ class HillshadePrepareProgram : public Program<
uniforms::matrix,
uniforms::dimension,
uniforms::zoom,
- uniforms::maxzoom>,
+ uniforms::maxzoom,
+ uniforms::unpack>,
TypeList<
textures::image>,
style::Properties<>> {
diff --git a/src/mbgl/programs/program_parameters.cpp b/src/mbgl/programs/program_parameters.cpp
index a49dc2bc88..95d2d0f668 100644
--- a/src/mbgl/programs/program_parameters.cpp
+++ b/src/mbgl/programs/program_parameters.cpp
@@ -4,8 +4,7 @@
namespace mbgl {
ProgramParameters::ProgramParameters(const float pixelRatio,
- const bool overdraw,
- optional<std::string> cacheDir_)
+ const bool overdraw)
: defines([&] {
std::string result;
result.reserve(32);
@@ -16,28 +15,11 @@ ProgramParameters::ProgramParameters(const float pixelRatio,
result += "#define OVERDRAW_INSPECTOR\n";
}
return result;
- }()),
- cacheDir(std::move(cacheDir_)) {
+ }()) {
}
const std::string& ProgramParameters::getDefines() const {
return defines;
}
-optional<std::string> ProgramParameters::cachePath(const char* name) const {
- if (!cacheDir) {
- return {};
- } else {
- std::string result;
- result.reserve(cacheDir->length() + 64);
- result += *cacheDir;
- result += "/com.mapbox.gl.shader.";
- result += name;
- result += '.';
- result += util::toHex(static_cast<uint64_t>(std::hash<std::string>()(defines)));
- result += ".pbf";
- return result;
- }
-}
-
} // namespace mbgl
diff --git a/src/mbgl/programs/program_parameters.hpp b/src/mbgl/programs/program_parameters.hpp
index 71ad454399..8af7ab50c0 100644
--- a/src/mbgl/programs/program_parameters.hpp
+++ b/src/mbgl/programs/program_parameters.hpp
@@ -8,14 +8,12 @@ namespace mbgl {
class ProgramParameters {
public:
- ProgramParameters(float pixelRatio, bool overdraw, optional<std::string> cacheDir);
+ ProgramParameters(float pixelRatio, bool overdraw);
const std::string& getDefines() const;
- optional<std::string> cachePath(const char* name) const;
private:
std::string defines;
- optional<std::string> cacheDir;
};
} // namespace mbgl
diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp
index 3633bd7c2a..bfc0133676 100644
--- a/src/mbgl/programs/symbol_program.cpp
+++ b/src/mbgl/programs/symbol_program.cpp
@@ -67,7 +67,7 @@ Values makeValues(const bool isText,
const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine;
mat4 labelPlaneMatrix;
- if (alongLine || (isText && hasVariablePacement)) {
+ if (alongLine || hasVariablePacement) {
// 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);
diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp
index d225ef963a..9019e76d3b 100644
--- a/src/mbgl/renderer/buckets/line_bucket.cpp
+++ b/src/mbgl/renderer/buckets/line_bucket.cpp
@@ -63,6 +63,9 @@ void LineBucket::addFeature(const GeometryTileFeature& feature,
const float COS_HALF_SHARP_CORNER = std::cos(75.0 / 2.0 * (M_PI / 180.0));
const float SHARP_CORNER_OFFSET = 15.0f;
+// Angle per triangle for approximating round line joins.
+const float DEG_PER_TRIANGLE = 20.0f;
+
// The number of bits that is used to store the line distance in the buffer.
const int LINE_DISTANCE_BUFFER_BITS = 14;
@@ -226,13 +229,18 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome
*
*/
- // Calculate the length of the miter (the ratio of the miter to the width).
- // Find the cosine of the angle between the next and join normals
- // using dot product. The inverse of that is the miter length.
+ // Calculate cosines of the angle (and its half) using dot product.
+ const double cosAngle = prevNormal->x * nextNormal->x + prevNormal->y * nextNormal->y;
const double cosHalfAngle = joinNormal.x * nextNormal->x + joinNormal.y * nextNormal->y;
+
+ // Calculate the length of the miter (the ratio of the miter to the width)
+ // as the inverse of cosine of the angle between next and join normals.
const double miterLength =
cosHalfAngle != 0 ? 1 / cosHalfAngle : std::numeric_limits<double>::infinity();
+ // Approximate angle from cosine.
+ const double approxAngle = 2 * std::sqrt(2 - 2 * cosHalfAngle);
+
const bool isSharpCorner = cosHalfAngle < COS_HALF_SHARP_CORNER && prevCoordinate && nextCoordinate;
if (isSharpCorner && i > first) {
@@ -331,20 +339,20 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome
// Create a round join by adding multiple pie slices. The join isn't actually round, but
// it looks like it is at the sizes we render lines at.
- // Add more triangles for sharper angles.
- // This math is just a good enough approximation. It isn't "correct".
- const int n = std::floor((0.5 - (cosHalfAngle - 0.5)) * 8);
-
- for (int m = 0; m < n; m++) {
- auto approxFractionalJoinNormal = util::unit(*nextNormal * ((m + 1.0) / (n + 1.0)) + *prevNormal);
- addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore, lineDistances);
- }
-
- addPieSliceVertex(*currentCoordinate, distance, joinNormal, lineTurnsLeft, startVertex, triangleStore, lineDistances);
-
- for (int k = n - 1; k >= 0; k--) {
- auto approxFractionalJoinNormal = util::unit(*prevNormal * ((k + 1.0) / (n + 1.0)) + *nextNormal);
- addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore, lineDistances);
+ // Pick the number of triangles for approximating round join by based on the angle between normals.
+ const unsigned n = ::round((approxAngle * 180 / M_PI) / DEG_PER_TRIANGLE);
+
+ for (unsigned m = 1; m < n; ++m) {
+ double t = double(m) / n;
+ if (t != 0.5) {
+ // approximate spherical interpolation https://observablehq.com/@mourner/approximating-geometric-slerp
+ const double t2 = t - 0.5;
+ const double A = 1.0904 + cosAngle * (-3.2452 + cosAngle * (3.55645 - cosAngle * 1.43519));
+ const double B = 0.848013 + cosAngle * (-1.06021 + cosAngle * 0.215638);
+ t = t + t * t2 * (t - 1) * (A * t2 * t2 + B);
+ }
+ auto approxFractionalNormal = util::unit(*prevNormal * (1.0 - t) + *nextNormal * t);
+ addPieSliceVertex(*currentCoordinate, distance, approxFractionalNormal, lineTurnsLeft, startVertex, triangleStore, lineDistances);
}
}
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index 83efb232c2..ff201e9c2c 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -18,15 +18,15 @@ SymbolBucket::SymbolBucket(Immutable<style::SymbolLayoutProperties::PossiblyEval
const style::PropertyValue<float>& textSize,
const style::PropertyValue<float>& iconSize,
float zoom,
- bool sdfIcons_,
bool iconsNeedLinear_,
bool sortFeaturesByY_,
const std::string bucketName_,
const std::vector<SymbolInstance>&& symbolInstances_,
- float tilePixelRatio_)
+ float tilePixelRatio_,
+ bool allowVerticalPlacement_,
+ std::vector<style::TextWritingModeType> placementModes_)
: layout(std::move(layout_)),
bucketLeaderID(std::move(bucketName_)),
- sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
sortFeaturesByY(sortFeaturesByY_),
staticUploaded(false),
@@ -39,7 +39,9 @@ SymbolBucket::SymbolBucket(Immutable<style::SymbolLayoutProperties::PossiblyEval
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())),
tilePixelRatio(tilePixelRatio_),
- bucketInstanceId(++maxBucketInstanceId) {
+ bucketInstanceId(++maxBucketInstanceId),
+ allowVerticalPlacement(allowVerticalPlacement_),
+ placementModes(std::move(placementModes_)) {
for (const auto& pair : paintProperties_) {
const auto& evaluated = getEvaluated<SymbolLayerProperties>(pair.second);
@@ -83,58 +85,78 @@ void SymbolBucket::upload(gfx::UploadPass& uploadPass) {
}
}
- if (hasIconData()) {
+ auto updateIconBuffer = [&](Buffer& iconBuffer) {
if (!staticUploaded) {
- icon.indexBuffer = uploadPass.createIndexBuffer(std::move(icon.triangles), sortFeaturesByY ? gfx::BufferUsageType::StreamDraw : gfx::BufferUsageType::StaticDraw);
- icon.vertexBuffer = uploadPass.createVertexBuffer(std::move(icon.vertices));
+ iconBuffer.indexBuffer = uploadPass.createIndexBuffer(std::move(iconBuffer.triangles), sortFeaturesByY ? gfx::BufferUsageType::StreamDraw : gfx::BufferUsageType::StaticDraw);
+ iconBuffer.vertexBuffer = uploadPass.createVertexBuffer(std::move(iconBuffer.vertices));
for (auto& pair : paintProperties) {
pair.second.iconBinders.upload(uploadPass);
}
} else if (!sortUploaded) {
- uploadPass.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles));
+ uploadPass.updateIndexBuffer(*iconBuffer.indexBuffer, std::move(iconBuffer.triangles));
}
if (!dynamicUploaded) {
- if (!icon.dynamicVertexBuffer) {
- icon.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(icon.dynamicVertices), gfx::BufferUsageType::StreamDraw);
+ if (!iconBuffer.dynamicVertexBuffer) {
+ iconBuffer.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(iconBuffer.dynamicVertices), gfx::BufferUsageType::StreamDraw);
} else {
- uploadPass.updateVertexBuffer(*icon.dynamicVertexBuffer, std::move(icon.dynamicVertices));
+ uploadPass.updateVertexBuffer(*iconBuffer.dynamicVertexBuffer, std::move(iconBuffer.dynamicVertices));
}
}
if (!placementChangesUploaded) {
- if (!icon.opacityVertexBuffer) {
- icon.opacityVertexBuffer = uploadPass.createVertexBuffer(std::move(icon.opacityVertices), gfx::BufferUsageType::StreamDraw);
+ if (!iconBuffer.opacityVertexBuffer) {
+ iconBuffer.opacityVertexBuffer = uploadPass.createVertexBuffer(std::move(iconBuffer.opacityVertices), gfx::BufferUsageType::StreamDraw);
} else {
- uploadPass.updateVertexBuffer(*icon.opacityVertexBuffer, std::move(icon.opacityVertices));
+ uploadPass.updateVertexBuffer(*iconBuffer.opacityVertexBuffer, std::move(iconBuffer.opacityVertices));
}
}
+ };
+ if (hasIconData()) {
+ updateIconBuffer(icon);
+ }
+ if (hasSdfIconData()) {
+ updateIconBuffer(sdfIcon);
}
- if (hasCollisionBoxData()) {
+ const auto updateCollisionBox = [&](CollisionBoxBuffer& collisionBox) {
if (!staticUploaded) {
- collisionBox->indexBuffer = uploadPass.createIndexBuffer(std::move(collisionBox->lines));
- collisionBox->vertexBuffer = uploadPass.createVertexBuffer(std::move(collisionBox->vertices));
+ collisionBox.indexBuffer = uploadPass.createIndexBuffer(std::move(collisionBox.lines));
+ collisionBox.vertexBuffer = uploadPass.createVertexBuffer(std::move(collisionBox.vertices));
}
if (!placementChangesUploaded) {
- if (!collisionBox->dynamicVertexBuffer) {
- collisionBox->dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(collisionBox->dynamicVertices), gfx::BufferUsageType::StreamDraw);
+ if (!collisionBox.dynamicVertexBuffer) {
+ collisionBox.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(collisionBox.dynamicVertices), gfx::BufferUsageType::StreamDraw);
} else {
- uploadPass.updateVertexBuffer(*collisionBox->dynamicVertexBuffer, std::move(collisionBox->dynamicVertices));
+ uploadPass.updateVertexBuffer(*collisionBox.dynamicVertexBuffer, std::move(collisionBox.dynamicVertices));
}
}
+ };
+ if (hasIconCollisionBoxData()) {
+ updateCollisionBox(*iconCollisionBox);
+ }
+
+ if (hasTextCollisionBoxData()) {
+ updateCollisionBox(*textCollisionBox);
}
- if (hasCollisionCircleData()) {
+ const auto updateCollisionCircle = [&](CollisionCircleBuffer& collisionCircle) {
if (!staticUploaded) {
- collisionCircle->indexBuffer = uploadPass.createIndexBuffer(std::move(collisionCircle->triangles));
- collisionCircle->vertexBuffer = uploadPass.createVertexBuffer(std::move(collisionCircle->vertices));
+ collisionCircle.indexBuffer = uploadPass.createIndexBuffer(std::move(collisionCircle.triangles));
+ collisionCircle.vertexBuffer = uploadPass.createVertexBuffer(std::move(collisionCircle.vertices));
}
if (!placementChangesUploaded) {
- if (!collisionCircle->dynamicVertexBuffer) {
- collisionCircle->dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(collisionCircle->dynamicVertices), gfx::BufferUsageType::StreamDraw);
+ if (!collisionCircle.dynamicVertexBuffer) {
+ collisionCircle.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(collisionCircle.dynamicVertices), gfx::BufferUsageType::StreamDraw);
} else {
- uploadPass.updateVertexBuffer(*collisionCircle->dynamicVertexBuffer, std::move(collisionCircle->dynamicVertices));
+ uploadPass.updateVertexBuffer(*collisionCircle.dynamicVertexBuffer, std::move(collisionCircle.dynamicVertices));
}
}
+ };
+ if (hasIconCollisionCircleData()) {
+ updateCollisionCircle(*iconCollisionCircle);
+ }
+
+ if (hasTextCollisionCircleData()) {
+ updateCollisionCircle(*textCollisionCircle);
}
uploaded = true;
@@ -145,7 +167,8 @@ void SymbolBucket::upload(gfx::UploadPass& uploadPass) {
}
bool SymbolBucket::hasData() const {
- return hasTextData() || hasIconData() || hasCollisionBoxData();
+ return hasTextData() || hasIconData() || hasSdfIconData() || hasIconCollisionBoxData() ||
+ hasTextCollisionBoxData() || hasIconCollisionCircleData() || hasTextCollisionCircleData();
}
bool SymbolBucket::hasTextData() const {
@@ -156,12 +179,24 @@ bool SymbolBucket::hasIconData() const {
return !icon.segments.empty();
}
-bool SymbolBucket::hasCollisionBoxData() const {
- return collisionBox && !collisionBox->segments.empty();
+bool SymbolBucket::hasSdfIconData() const {
+ return !sdfIcon.segments.empty();
}
-bool SymbolBucket::hasCollisionCircleData() const {
- return collisionCircle && !collisionCircle->segments.empty();
+bool SymbolBucket::hasIconCollisionBoxData() const {
+ return iconCollisionBox && !iconCollisionBox->segments.empty();
+}
+
+bool SymbolBucket::hasIconCollisionCircleData() const {
+ return iconCollisionCircle && !iconCollisionCircle->segments.empty();
+}
+
+bool SymbolBucket::hasTextCollisionBoxData() const {
+ return textCollisionBox && !textCollisionBox->segments.empty();
+}
+
+bool SymbolBucket::hasTextCollisionCircleData() const {
+ return textCollisionCircle && !textCollisionCircle->segments.empty();
}
void addPlacedSymbol(gfx::IndexVector<gfx::Triangles>& triangles, const PlacedSymbol& placedSymbol) {
@@ -184,9 +219,9 @@ void SymbolBucket::sortFeatures(const float angle) {
sortedAngle = angle;
- // The current approach to sorting doesn't sort across segments so don't try.
+ // The current approach to sorting doesn't sort across text and icon segments so don't try.
// Sorting within segments separately seemed not to be worth the complexity.
- if (text.segments.size() > 1 || icon.segments.size() > 1) {
+ if (text.segments.size() > 1 || (icon.segments.size() > 1 || sdfIcon.segments.size() > 1)) {
return;
}
@@ -195,6 +230,7 @@ void SymbolBucket::sortFeatures(const float angle) {
text.triangles.clear();
icon.triangles.clear();
+ sdfIcon.triangles.clear();
featureSortOrder = std::make_unique<std::vector<size_t>>();
featureSortOrder->reserve(symbolInstances.size());
@@ -221,14 +257,19 @@ void SymbolBucket::sortFeatures(const float angle) {
addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]);
}
+ auto& iconBuffer = symbolInstance.hasSdfIcon() ? sdfIcon : icon;
if (symbolInstance.placedIconIndex) {
- addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]);
+ addPlacedSymbol(iconBuffer.triangles, iconBuffer.placedSymbols[*symbolInstance.placedIconIndex]);
+ }
+
+ if (symbolInstance.placedVerticalIconIndex) {
+ addPlacedSymbol(iconBuffer.triangles, iconBuffer.placedSymbols[*symbolInstance.placedVerticalIconIndex]);
}
}
}
-std::vector<std::reference_wrapper<SymbolInstance>> SymbolBucket::getSortedSymbols(const float angle) {
- std::vector<std::reference_wrapper<SymbolInstance>> result(symbolInstances.begin(), symbolInstances.end());
+std::vector<std::reference_wrapper<const SymbolInstance>> SymbolBucket::getSortedSymbols(const float angle) const {
+ std::vector<std::reference_wrapper<const SymbolInstance>> result(symbolInstances.begin(), symbolInstances.end());
const float sin = std::sin(angle);
const float cos = std::cos(angle);
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index c22a168a0c..020190b81f 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -21,9 +21,9 @@ class CrossTileSymbolLayerIndex;
class PlacedSymbol {
public:
PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
- std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
+ std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_, optional<size_t> placedIconIndex_ = nullopt) :
anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
- lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0)
+ lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0), placedIconIndex(std::move(placedIconIndex_))
{
}
Point<float> anchorPoint;
@@ -39,6 +39,13 @@ public:
size_t vertexStartIndex;
// The crossTileID is only filled/used on the foreground for variable text anchors
uint32_t crossTileID = 0u;
+ // The placedOrientation is only used when symbol layer's property is set to support
+ // placement for orientation variants.
+ optional<style::TextWritingModeType> placedOrientation;
+ float angle = 0;
+
+ // Reference to placed icon, only applicable for text symbols.
+ optional<size_t> placedIconIndex;
};
class SymbolBucket final : public Bucket {
@@ -48,12 +55,13 @@ public:
const style::PropertyValue<float>& textSize,
const style::PropertyValue<float>& iconSize,
float zoom,
- bool sdfIcons,
bool iconsNeedLinear,
bool sortFeaturesByY,
const std::string bucketLeaderID,
const std::vector<SymbolInstance>&&,
- const float tilePixelRatio);
+ const float tilePixelRatio,
+ bool allowVerticalPlacement,
+ std::vector<style::TextWritingModeType> placementModes);
~SymbolBucket() override;
void upload(gfx::UploadPass&) override;
@@ -63,28 +71,31 @@ public:
void updateVertices(Placement&, bool updateOpacities, const TransformState&, const RenderTile&, std::set<uint32_t>&) override;
bool hasTextData() const;
bool hasIconData() const;
- bool hasCollisionBoxData() const;
- bool hasCollisionCircleData() const;
+ bool hasSdfIconData() const;
+ bool hasIconCollisionBoxData() const;
+ bool hasIconCollisionCircleData() const;
+ bool hasTextCollisionBoxData() const;
+ bool hasTextCollisionCircleData() const;
bool hasFormatSectionOverrides() const;
void sortFeatures(const float angle);
// The result contains references to the `symbolInstances` items, sorted by viewport Y.
- std::vector<std::reference_wrapper<SymbolInstance>> getSortedSymbols(const float angle);
+ std::vector<std::reference_wrapper<const SymbolInstance>> getSortedSymbols(const float angle) const;
Immutable<style::SymbolLayoutProperties::PossiblyEvaluated> layout;
const std::string bucketLeaderID;
float sortedAngle = std::numeric_limits<float>::max();
// Flags
- const bool sdfIcons : 1;
const bool iconsNeedLinear : 1;
const bool sortFeaturesByY : 1;
bool staticUploaded : 1;
bool placementChangesUploaded : 1;
bool dynamicUploaded : 1;
bool sortUploaded : 1;
- bool justReloaded : 1;
+ // Set and used by placement.
+ mutable bool justReloaded : 1;
bool hasVariablePlacement : 1;
std::vector<SymbolInstance> symbolInstances;
@@ -113,9 +124,9 @@ public:
std::unique_ptr<SymbolSizeBinder> iconSizeBinder;
- struct IconBuffer : public Buffer {
- } icon;
-
+ Buffer icon;
+ Buffer sdfIcon;
+
struct CollisionBuffer {
gfx::VertexVector<gfx::Vertex<CollisionBoxLayoutAttributes>> vertices;
gfx::VertexVector<gfx::Vertex<CollisionBoxDynamicAttributes>> dynamicVertices;
@@ -129,27 +140,40 @@ public:
gfx::IndexVector<gfx::Lines> lines;
optional<gfx::IndexBuffer> indexBuffer;
};
- std::unique_ptr<CollisionBoxBuffer> collisionBox;
+ std::unique_ptr<CollisionBoxBuffer> iconCollisionBox;
+ std::unique_ptr<CollisionBoxBuffer> textCollisionBox;
+
+ CollisionBoxBuffer& getOrCreateIconCollisionBox() {
+ if (!iconCollisionBox) iconCollisionBox = std::make_unique<CollisionBoxBuffer>();
+ return *iconCollisionBox;
+ }
- CollisionBoxBuffer& getOrCreateCollisionBox() {
- if (!collisionBox) collisionBox = std::make_unique<CollisionBoxBuffer>();
- return *collisionBox;
+ CollisionBoxBuffer& getOrCreateTextCollisionBox() {
+ if (!textCollisionBox) textCollisionBox = std::make_unique<CollisionBoxBuffer>();
+ return *textCollisionBox;
}
struct CollisionCircleBuffer : public CollisionBuffer {
gfx::IndexVector<gfx::Triangles> triangles;
optional<gfx::IndexBuffer> indexBuffer;
};
- std::unique_ptr<CollisionCircleBuffer> collisionCircle;
+ std::unique_ptr<CollisionCircleBuffer> iconCollisionCircle;
+ std::unique_ptr<CollisionCircleBuffer> textCollisionCircle;
- CollisionCircleBuffer& getOrCreateCollisionCircleBuffer() {
- if (!collisionCircle) collisionCircle = std::make_unique<CollisionCircleBuffer>();
- return *collisionCircle;
+ CollisionCircleBuffer& getOrCreateIconCollisionCircleBuffer() {
+ if (!iconCollisionCircle) iconCollisionCircle = std::make_unique<CollisionCircleBuffer>();
+ return *iconCollisionCircle;
+ }
+
+ CollisionCircleBuffer& getOrCreateTextCollisionCircleBuffer() {
+ if (!textCollisionCircle) textCollisionCircle = std::make_unique<CollisionCircleBuffer>();
+ return *textCollisionCircle;
}
const float tilePixelRatio;
uint32_t bucketInstanceId;
-
+ const bool allowVerticalPlacement;
+ const std::vector<style::TextWritingModeType> placementModes;
mutable optional<bool> hasFormatSectionOverrides_;
std::shared_ptr<std::vector<size_t>> featureSortOrder;
diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp
index 3c22cdcf32..4bbdbca5d9 100644
--- a/src/mbgl/renderer/image_manager.cpp
+++ b/src/mbgl/renderer/image_manager.cpp
@@ -81,6 +81,7 @@ void ImageManager::removeImage(const std::string& id) {
requestedImages.erase(requestedIt);
}
images.erase(it);
+ updatedImageVersions.erase(id);
}
const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp
index 885b6fe9b2..6f76efda7a 100644
--- a/src/mbgl/renderer/layers/render_background_layer.cpp
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -16,10 +16,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const BackgroundLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == BackgroundLayer::Impl::staticTypeInfo());
return static_cast<const style::BackgroundLayer::Impl&>(*impl);
}
+} // namespace
+
RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl)
: RenderLayer(makeMutable<BackgroundLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()) {
@@ -37,8 +42,14 @@ void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters &paramet
parameters.getCrossfadeParameters(),
unevaluated.evaluate(parameters));
- passes = properties->evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent
- : RenderPass::None;
+ passes = properties->evaluated.get<style::BackgroundOpacity>() == 0.0f
+ ? RenderPass::None
+ : (!unevaluated.get<style::BackgroundPattern>().isUndefined()
+ || properties->evaluated.get<style::BackgroundOpacity>() < 1.0f
+ || properties->evaluated.get<style::BackgroundColor>().a < 1.0f)
+ ? RenderPass::Translucent
+ // Supply both - evaluated based on opaquePassCutoff in render().
+ : RenderPass::Opaque | RenderPass::Translucent;
properties->renderPasses = mbgl::underlying_type(passes);
evaluatedProperties = std::move(properties);
}
@@ -77,7 +88,9 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) {
parameters.context,
*parameters.renderPass,
gfx::Triangles(),
- parameters.depthModeForSublayer(0, gfx::DepthMaskType::ReadOnly),
+ parameters.depthModeForSublayer(0, parameters.pass == RenderPass::Opaque
+ ? gfx::DepthMaskType::ReadWrite
+ : gfx::DepthMaskType::ReadOnly),
gfx::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
gfx::CullFaceMode::disabled(),
@@ -118,6 +131,12 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) {
);
}
} else {
+ auto backgroundRenderPass = (evaluated.get<BackgroundColor>().a >= 1.0f
+ && evaluated.get<BackgroundOpacity>() >= 1.0f
+ && parameters.currentLayer >= parameters.opaquePassCutoff) ? RenderPass::Opaque : RenderPass::Translucent;
+ if (parameters.pass != backgroundRenderPass) {
+ return;
+ }
for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
draw(
parameters.programs.getBackgroundLayerPrograms().background,
diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp
index 4f1620364f..ea0c20f124 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.cpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.cpp
@@ -15,10 +15,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const style::CircleLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == CircleLayer::Impl::staticTypeInfo());
return static_cast<const style::CircleLayer::Impl&>(*impl);
}
+} // namespace
+
RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl)
: RenderLayer(makeMutable<CircleLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()) {
diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp
index 75c21997b0..c53286a2a0 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.cpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -14,10 +14,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const CustomLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == CustomLayer::Impl::staticTypeInfo());
return static_cast<const CustomLayer::Impl&>(*impl);
}
+} // namespace
+
RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
: RenderLayer(makeMutable<CustomLayerProperties>(std::move(_impl))),
host(impl(baseImpl).host) {
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
index 3c97ab7431..835e8c8ee5 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -20,10 +20,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const FillExtrusionLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == FillExtrusionLayer::Impl::staticTypeInfo());
return static_cast<const FillExtrusionLayer::Impl&>(*impl);
}
+} // namespace
+
RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl)
: RenderLayer(makeMutable<FillExtrusionLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()) {
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
index cf31201d77..27cb76fede 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -21,10 +21,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const FillLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == FillLayer::Impl::staticTypeInfo());
return static_cast<const FillLayer::Impl&>(*impl);
}
+} // namespace
+
RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl)
: RenderLayer(makeMutable<FillLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()) {
@@ -47,17 +52,12 @@ void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) {
evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>();
}
- passes = RenderPass::None;
+ passes = RenderPass::Translucent;
- if (evaluated.get<style::FillAntialias>()) {
- passes |= RenderPass::Translucent;
- }
-
- if (!unevaluated.get<style::FillPattern>().isUndefined()
+ 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 {
+ || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f)) {
+ // Supply both - evaluated based on opaquePassCutoff in render().
passes |= RenderPass::Opaque;
}
properties->renderPasses = mbgl::underlying_type(passes);
diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
index 478a8f8c47..4fa20ffd13 100644
--- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp
+++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
@@ -19,10 +19,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const HeatmapLayer::Impl& impl(const Immutable<Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == HeatmapLayer::Impl::staticTypeInfo());
return static_cast<const HeatmapLayer::Impl&>(*impl);
}
+} // namespace
+
RenderHeatmapLayer::RenderHeatmapLayer(Immutable<HeatmapLayer::Impl> _impl)
: RenderLayer(makeMutable<HeatmapLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()), colorRamp({256, 1}) {
diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp
index 2c7e0aef86..b570d9c4c9 100644
--- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp
+++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp
@@ -18,10 +18,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const HillshadeLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == HillshadeLayer::Impl::staticTypeInfo());
return static_cast<const HillshadeLayer::Impl&>(*impl);
}
+} // namespace
+
RenderHillshadeLayer::RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl> _impl)
: RenderLayer(makeMutable<HillshadeLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()) {
@@ -158,6 +163,7 @@ void RenderHillshadeLayer::render(PaintParameters& parameters) {
uniforms::dimension::Value( {{stride, stride}} ),
uniforms::zoom::Value( float(tile.id.canonical.z) ),
uniforms::maxzoom::Value( float(maxzoom) ),
+ uniforms::unpack::Value( bucket.getDEMData().getUnpackVector() ),
},
paintAttributeData,
properties,
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
index fcd52b21df..6d635f65e7 100644
--- a/src/mbgl/renderer/layers/render_line_layer.cpp
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -20,10 +20,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const LineLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == LineLayer::Impl::staticTypeInfo());
return static_cast<const LineLayer::Impl&>(*impl);
}
+} // namespace
+
RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl)
: RenderLayer(makeMutable<LineLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()),
diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp
index 82d135b9ef..8a1a8a6c28 100644
--- a/src/mbgl/renderer/layers/render_raster_layer.cpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -14,10 +14,15 @@ namespace mbgl {
using namespace style;
+namespace {
+
inline const RasterLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == RasterLayer::Impl::staticTypeInfo());
return static_cast<const RasterLayer::Impl&>(*impl);
}
+} // namespace
+
RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl)
: RenderLayer(makeMutable<RasterLayerProperties>(std::move(_impl))),
unevaluated(impl(baseImpl).paint.untransitioned()) {
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
index 4dddd57009..06eacc041b 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.cpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -67,20 +67,20 @@ struct RenderableSegment {
const LayerRenderData& renderData_,
const SymbolBucket::PaintProperties& bucketPaintProperties_,
float sortKey_,
- bool isText_) :
+ const SymbolType type_) :
segment(std::move(segment_)),
tile(tile_),
renderData(renderData_),
bucketPaintProperties(bucketPaintProperties_),
sortKey(sortKey_),
- isText(isText_) {}
+ type(type_) {}
SegmentWrapper segment;
const RenderTile& tile;
const LayerRenderData& renderData;
const SymbolBucket::PaintProperties& bucketPaintProperties;
float sortKey;
- bool isText;
+ SymbolType type;
friend bool operator < (const RenderableSegment& lhs, const RenderableSegment& rhs) {
// Sort renderable segments by a sort key.
@@ -91,11 +91,11 @@ struct RenderableSegment {
// In cases when sort key is the same, sort by the type of a segment (text over icons),
// and for segments of the same type with the same sort key, sort by a tile id.
if (lhs.sortKey == rhs.sortKey) {
- if (!lhs.isText && rhs.isText) {
+ if (lhs.type != SymbolType::Text && rhs.type == SymbolType::Text) {
return true;
}
- if (lhs.isText == rhs.isText) {
+ if (lhs.type == rhs.type) {
return lhs.tile.id < rhs.tile.id;
}
}
@@ -110,7 +110,8 @@ void drawIcon(const DrawFn& draw,
const LayerRenderData& renderData,
SegmentsWrapper iconSegments,
const SymbolBucket::PaintProperties& bucketPaintProperties,
- const PaintParameters& parameters) {
+ const PaintParameters& parameters,
+ const bool sdfIcons) {
auto& bucket = static_cast<SymbolBucket&>(*renderData.bucket);
const auto& evaluated = getEvaluated<SymbolLayerProperties>(renderData.layerProperties);
const auto& layout = *bucket.layout;
@@ -124,19 +125,20 @@ void drawIcon(const DrawFn& draw,
const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0;
const gfx::TextureBinding textureBinding{ tile.getIconAtlasTexture().getResource(),
- bucket.sdfIcons ||
+ sdfIcons ||
parameters.state.isChanging() ||
iconScaled || iconTransformed
? gfx::TextureFilterType::Linear
: gfx::TextureFilterType::Nearest };
const Size& iconSize = tile.getIconAtlasTexture().size;
+ const bool variablePlacedIcon = bucket.hasVariablePlacement && layout.get<IconTextFit>() != IconTextFitType::None;
- if (bucket.sdfIcons) {
+ if (sdfIcons) {
if (values.hasHalo) {
draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF,
- SymbolSDFIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
- bucket.icon,
+ SymbolSDFIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
+ bucket.sdfIcon,
iconSegments,
bucket.iconSizeBinder,
bucketPaintProperties.iconBinders,
@@ -149,8 +151,8 @@ void drawIcon(const DrawFn& draw,
if (values.hasFill) {
draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF,
- SymbolSDFIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
- bucket.icon,
+ SymbolSDFIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
+ bucket.sdfIcon,
iconSegments,
bucket.iconSizeBinder,
bucketPaintProperties.iconBinders,
@@ -162,7 +164,7 @@ void drawIcon(const DrawFn& draw,
}
} else {
draw(parameters.programs.getSymbolLayerPrograms().symbolIcon,
- SymbolIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange),
+ SymbolIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange),
bucket.icon,
iconSegments,
bucket.iconSizeBinder,
@@ -227,6 +229,7 @@ void drawText(const DrawFn& draw,
}
inline const SymbolLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl) {
+ assert(impl->getTypeInfo() == SymbolLayer::Impl::staticTypeInfo());
return static_cast<const SymbolLayer::Impl&>(*impl);
}
@@ -359,68 +362,41 @@ void RenderSymbolLayer::render(PaintParameters& parameters) {
assert(bucket.paintProperties.find(getID()) != bucket.paintProperties.end());
const auto& bucketPaintProperties = bucket.paintProperties.at(getID());
- auto addRenderables = [&renderableSegments, &tile, renderData, &bucketPaintProperties, it = renderableSegments.begin()] (auto& segments, bool isText) mutable {
+ auto addRenderables = [&renderableSegments, &tile, renderData, &bucketPaintProperties, it = renderableSegments.begin()] (auto& segments, const SymbolType type) mutable {
for (auto& segment : segments) {
- it = renderableSegments.emplace_hint(it, SegmentWrapper{std::ref(segment)}, tile, *renderData, bucketPaintProperties, segment.sortKey, isText);
+ it = renderableSegments.emplace_hint(it, SegmentWrapper{std::ref(segment)}, tile, *renderData, bucketPaintProperties, segment.sortKey, type);
}
};
if (bucket.hasIconData()) {
if (sortFeaturesByKey) {
- addRenderables(bucket.icon.segments, false /*isText*/);
+ addRenderables(bucket.icon.segments, SymbolType::IconRGBA);
+ } else {
+ drawIcon(draw, tile, *renderData, std::ref(bucket.icon.segments), bucketPaintProperties, parameters, false /*sdfIcon*/);
+ }
+ }
+
+ if (bucket.hasSdfIconData()) {
+ if (sortFeaturesByKey) {
+ addRenderables(bucket.sdfIcon.segments, SymbolType::IconSDF);
} else {
- drawIcon(draw, tile, *renderData, std::ref(bucket.icon.segments), bucketPaintProperties, parameters);
+ drawIcon(draw, tile, *renderData, std::ref(bucket.sdfIcon.segments), bucketPaintProperties, parameters, true /*sdfIcon*/);
}
}
if (bucket.hasTextData()) {
if (sortFeaturesByKey) {
- addRenderables(bucket.text.segments, true /*isText*/);
+ addRenderables(bucket.text.segments, SymbolType::Text);
} else {
drawText(draw, tile, *renderData, std::ref(bucket.text.segments), bucketPaintProperties, parameters);
}
}
- if (bucket.hasCollisionBoxData()) {
- static const style::Properties<>::PossiblyEvaluated properties {};
- static const CollisionBoxProgram::Binders paintAttributeData(properties, 0);
+ const auto drawCollisonData = [&](const bool isText, const bool hasCollisionBox, const bool hasCollisionCircle) {
+ if (!hasCollisionBox && !hasCollisionCircle) return;
- auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
- const float scale = std::pow(2, parameters.state.getZoom() - tile.getOverscaledTileID().overscaledZ);
- std::array<float,2> extrudeScale =
- {{
- parameters.pixelsToGLUnits[0] / (pixelRatio * scale),
- parameters.pixelsToGLUnits[1] / (pixelRatio * scale)
- }};
- parameters.programs.getSymbolLayerPrograms().collisionBox.draw(
- parameters.context,
- *parameters.renderPass,
- gfx::Lines { 1.0f },
- gfx::DepthMode::disabled(),
- gfx::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
- gfx::CullFaceMode::disabled(),
- CollisionBoxProgram::LayoutUniformValues {
- uniforms::matrix::Value( tile.matrix ),
- uniforms::extrude_scale::Value( extrudeScale ),
- uniforms::camera_to_center_distance::Value( parameters.state.getCameraToCenterDistance() )
- },
- *bucket.collisionBox->vertexBuffer,
- *bucket.collisionBox->dynamicVertexBuffer,
- *bucket.collisionBox->indexBuffer,
- bucket.collisionBox->segments,
- paintAttributeData,
- properties,
- CollisionBoxProgram::TextureBindings{},
- parameters.state.getZoom(),
- getID()
- );
- }
-
- if (bucket.hasCollisionCircleData()) {
- static const style::Properties<>::PossiblyEvaluated properties {};
+ static const style::Properties<>::PossiblyEvaluated properties{};
static const CollisionBoxProgram::Binders paintAttributeData(properties, 0);
-
auto pixelRatio = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
const float scale = std::pow(2, parameters.state.getZoom() - tile.getOverscaledTileID().overscaledZ);
std::array<float,2> extrudeScale =
@@ -428,40 +404,77 @@ void RenderSymbolLayer::render(PaintParameters& parameters) {
parameters.pixelsToGLUnits[0] / (pixelRatio * scale),
parameters.pixelsToGLUnits[1] / (pixelRatio * scale)
}};
-
- parameters.programs.getSymbolLayerPrograms().collisionCircle.draw(
- parameters.context,
- *parameters.renderPass,
- gfx::Triangles(),
- gfx::DepthMode::disabled(),
- gfx::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
- gfx::CullFaceMode::disabled(),
- CollisionCircleProgram::LayoutUniformValues {
- uniforms::matrix::Value( tile.matrix ),
- uniforms::extrude_scale::Value( extrudeScale ),
- uniforms::overscale_factor::Value( float(tile.getOverscaledTileID().overscaleFactor()) ),
- uniforms::camera_to_center_distance::Value( parameters.state.getCameraToCenterDistance() )
- },
- *bucket.collisionCircle->vertexBuffer,
- *bucket.collisionCircle->dynamicVertexBuffer,
- *bucket.collisionCircle->indexBuffer,
- bucket.collisionCircle->segments,
- paintAttributeData,
- properties,
- CollisionCircleProgram::TextureBindings{},
- parameters.state.getZoom(),
- getID()
- );
- }
+ const auto& evaluated = getEvaluated<SymbolLayerProperties>(renderData->layerProperties);
+ const auto& layout = *bucket.layout;
+ const auto values = isText ? textPropertyValues(evaluated, layout): iconPropertyValues(evaluated, layout);
+ const bool needTranslate = values.translate[0] != 0 || values.translate[1] != 0;
+
+ if (hasCollisionBox) {
+ const auto& collisionBox = isText ? bucket.textCollisionBox : bucket.iconCollisionBox;
+ parameters.programs.getSymbolLayerPrograms().collisionBox.draw(
+ parameters.context,
+ *parameters.renderPass,
+ gfx::Lines{ 1.0f },
+ gfx::DepthMode::disabled(),
+ gfx::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ gfx::CullFaceMode::disabled(),
+ CollisionBoxProgram::LayoutUniformValues {
+ uniforms::matrix::Value((needTranslate
+ ? tile.translatedMatrix(values.translate, values.translateAnchor, parameters.state)
+ : tile.matrix)),
+ uniforms::extrude_scale::Value(extrudeScale),
+ uniforms::camera_to_center_distance::Value(parameters.state.getCameraToCenterDistance())
+ },
+ *collisionBox->vertexBuffer,
+ *collisionBox->dynamicVertexBuffer,
+ *collisionBox->indexBuffer,
+ collisionBox->segments,
+ paintAttributeData,
+ properties,
+ CollisionBoxProgram::TextureBindings{},
+ parameters.state.getZoom(),
+ getID());
+ }
+ if (hasCollisionCircle) {
+ const auto& collisionCircle = isText ? bucket.textCollisionCircle : bucket.iconCollisionCircle;
+ parameters.programs.getSymbolLayerPrograms().collisionCircle.draw(
+ parameters.context,
+ *parameters.renderPass,
+ gfx::Triangles(),
+ gfx::DepthMode::disabled(),
+ gfx::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ gfx::CullFaceMode::disabled(),
+ CollisionCircleProgram::LayoutUniformValues {
+ uniforms::matrix::Value((needTranslate
+ ? tile.translatedMatrix(values.translate, values.translateAnchor, parameters.state)
+ : tile.matrix)),
+ uniforms::extrude_scale::Value(extrudeScale),
+ uniforms::overscale_factor::Value(float(tile.getOverscaledTileID().overscaleFactor())),
+ uniforms::camera_to_center_distance::Value(parameters.state.getCameraToCenterDistance())
+ },
+ *collisionCircle->vertexBuffer,
+ *collisionCircle->dynamicVertexBuffer,
+ *collisionCircle->indexBuffer,
+ collisionCircle->segments,
+ paintAttributeData,
+ properties,
+ CollisionCircleProgram::TextureBindings{},
+ parameters.state.getZoom(),
+ getID());
+ }
+ };
+ drawCollisonData(false /*isText*/, bucket.hasIconCollisionBoxData(), bucket.hasIconCollisionCircleData());
+ drawCollisonData(true /*isText*/, bucket.hasTextCollisionBoxData(), bucket.hasTextCollisionCircleData());
}
if (sortFeaturesByKey) {
for (auto& renderable : renderableSegments) {
- if (renderable.isText) {
+ if (renderable.type == SymbolType::Text) {
drawText(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters);
} else {
- drawIcon(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters);
+ drawIcon(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters, renderable.type == SymbolType::IconSDF);
}
}
}
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp
index d9ce8c688d..5fcb9e2c8c 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.hpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp
@@ -50,6 +50,12 @@ public:
bool hasFill;
};
+enum class SymbolType : uint8_t {
+ Text,
+ IconRGBA,
+ IconSDF
+};
+
} // namespace style
class RenderSymbolLayer final: public RenderLayer {
diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp
index c1ca1fd017..fe1b151b58 100644
--- a/src/mbgl/renderer/render_layer.cpp
+++ b/src/mbgl/renderer/render_layer.cpp
@@ -32,7 +32,7 @@ const std::string& RenderLayer::getID() const {
}
bool RenderLayer::hasRenderPass(RenderPass pass) const {
- return bool(passes & pass);
+ return passes & pass;
}
bool RenderLayer::needsRendering() const {
diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp
index 5b6b27c185..a62ccb0a0a 100644
--- a/src/mbgl/renderer/render_orchestrator.cpp
+++ b/src/mbgl/renderer/render_orchestrator.cpp
@@ -360,28 +360,33 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(const UpdatePar
crossTileSymbolIndex.reset();
}
+ bool symbolBucketsAdded = false;
bool symbolBucketsChanged = false;
- const bool placementChanged = !placement->stillRecent(updateParameters.timePoint);
+ for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) {
+ auto result = crossTileSymbolIndex.addLayer(*it, updateParameters.transformState.getLatLng().longitude());
+ symbolBucketsAdded = symbolBucketsAdded || (result & CrossTileSymbolIndex::AddLayerResult::BucketsAdded);
+ symbolBucketsChanged = symbolBucketsChanged || (result != CrossTileSymbolIndex::AddLayerResult::NoChanges);
+ }
+ // We want new symbols to show up faster, however simple setting `placementChanged` to `true` would
+ // initiate placement too often as new buckets ususally come from several rendered tiles in a row within
+ // a short period of time. Instead, we squeeze placement update period to coalesce buckets updates from several tiles.
+ if (symbolBucketsAdded) placement->setMaximumUpdatePeriod(Milliseconds(30));
+ renderTreeParameters->placementChanged = !placement->stillRecent(updateParameters.timePoint, updateParameters.transformState.getZoom());
+
std::set<std::string> usedSymbolLayers;
- if (placementChanged) {
+ if (renderTreeParameters->placementChanged) {
placement = std::make_unique<Placement>(
updateParameters.transformState, updateParameters.mode,
updateParameters.transitionOptions, updateParameters.crossSourceCollisions,
std::move(placement));
- }
- for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) {
- const RenderLayer& layer = *it;
- if (crossTileSymbolIndex.addLayer(layer, updateParameters.transformState.getLatLng().longitude())) symbolBucketsChanged = true;
-
- if (placementChanged) {
+ for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) {
+ const RenderLayer& layer = *it;
usedSymbolLayers.insert(layer.getID());
placement->placeLayer(layer, renderTreeParameters->transformParams.projMatrix, updateParameters.debugOptions & MapDebugOptions::Collision);
}
- }
- if (placementChanged) {
- placement->commit(updateParameters.timePoint);
+ placement->commit(updateParameters.timePoint, updateParameters.transformState.getZoom());
crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers);
for (const auto& entry : renderSources) {
entry.second->updateFadingTiles();
@@ -391,7 +396,7 @@ std::unique_ptr<RenderTree> RenderOrchestrator::createRenderTree(const UpdatePar
}
for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) {
- placement->updateLayerBuckets(*it, updateParameters.transformState, placementChanged || symbolBucketsChanged);
+ placement->updateLayerBuckets(*it, updateParameters.transformState, renderTreeParameters->placementChanged || symbolBucketsChanged);
}
renderTreeParameters->symbolFadeChange = placement->symbolFadeChange(updateParameters.timePoint);
diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp
index 5d18304129..4d1b1f91f9 100644
--- a/src/mbgl/renderer/render_pass.hpp
+++ b/src/mbgl/renderer/render_pass.hpp
@@ -1,7 +1,6 @@
#pragma once
-#include <mbgl/util/traits.hpp>
-#include <mbgl/util/util.hpp>
+#include <mbgl/util/bitmask_operations.hpp>
#include <cstdint>
@@ -14,18 +13,6 @@ enum class RenderPass : uint8_t {
Pass3D = 1 << 2,
};
-MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) {
- return RenderPass(mbgl::underlying_type(a) | mbgl::underlying_type(b));
-}
-
-MBGL_CONSTEXPR RenderPass& operator|=(RenderPass& a, RenderPass b) {
- return (a = a | b);
-}
-
-MBGL_CONSTEXPR RenderPass operator&(RenderPass a, RenderPass b) {
- return RenderPass(mbgl::underlying_type(a) & mbgl::underlying_type(b));
-}
-
// Defines whether the overdraw shaders should be used instead of the regular shaders.
enum class PaintMode : bool {
Regular = false,
diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp
index 9090ef6579..6378ad9989 100644
--- a/src/mbgl/renderer/render_static_data.cpp
+++ b/src/mbgl/renderer/render_static_data.cpp
@@ -49,10 +49,10 @@ static gfx::VertexVector<HeatmapTextureLayoutVertex> heatmapTextureVertices() {
return result;
}
-RenderStaticData::RenderStaticData(gfx::Context& context, float pixelRatio, const optional<std::string>& programCacheDir)
- : programs(context, ProgramParameters { pixelRatio, false, programCacheDir })
+RenderStaticData::RenderStaticData(gfx::Context& context, float pixelRatio)
+ : programs(context, ProgramParameters { pixelRatio, false })
#ifndef NDEBUG
- , overdrawPrograms(context, ProgramParameters { pixelRatio, true, programCacheDir })
+ , overdrawPrograms(context, ProgramParameters { pixelRatio, true })
#endif
{
tileTriangleSegments.emplace_back(0, 0, 4, 6);
diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp
index 5b409933af..6bf2c846f4 100644
--- a/src/mbgl/renderer/render_static_data.hpp
+++ b/src/mbgl/renderer/render_static_data.hpp
@@ -19,7 +19,7 @@ class UploadPass;
class RenderStaticData {
public:
- RenderStaticData(gfx::Context&, float pixelRatio, const optional<std::string>& programCacheDir);
+ RenderStaticData(gfx::Context&, float pixelRatio);
void upload(gfx::UploadPass&);
diff --git a/src/mbgl/renderer/render_tree.hpp b/src/mbgl/renderer/render_tree.hpp
index cf62ccb03e..557442c0fa 100644
--- a/src/mbgl/renderer/render_tree.hpp
+++ b/src/mbgl/renderer/render_tree.hpp
@@ -52,6 +52,7 @@ public:
float symbolFadeChange = 0.0f;
bool needsRepaint = false;
bool loaded = false;
+ bool placementChanged = false;
};
class RenderTree {
diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp
index dd715bd3bc..52cd7a4351 100644
--- a/src/mbgl/renderer/renderer.cpp
+++ b/src/mbgl/renderer/renderer.cpp
@@ -10,11 +10,9 @@ namespace mbgl {
Renderer::Renderer(gfx::RendererBackend& backend,
float pixelRatio_,
- const optional<std::string> programCacheDir_,
const optional<std::string> localFontFamily_)
: impl(std::make_unique<Impl>(backend,
pixelRatio_,
- std::move(programCacheDir_),
std::move(localFontFamily_))) {
}
diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp
index 990d9cd3ab..113840d059 100644
--- a/src/mbgl/renderer/renderer_impl.cpp
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -26,13 +26,11 @@ static RendererObserver& nullObserver() {
Renderer::Impl::Impl(gfx::RendererBackend& backend_,
float pixelRatio_,
- optional<std::string> programCacheDir_,
optional<std::string> localFontFamily_)
: orchestrator(!backend_.contextIsShared(), std::move(localFontFamily_))
, backend(backend_)
, observer(&nullObserver())
- , pixelRatio(pixelRatio_)
- , programCacheDir(std::move(programCacheDir_)) {
+ , pixelRatio(pixelRatio_) {
}
@@ -53,7 +51,7 @@ void Renderer::Impl::render(const RenderTree& renderTree) {
const auto& renderTreeParameters = renderTree.getParameters();
if (!staticData) {
- staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio, programCacheDir);
+ staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio);
}
staticData->has3D = renderTreeParameters.has3D;
@@ -212,7 +210,8 @@ void Renderer::Impl::render(const RenderTree& renderTree) {
observer->onDidFinishRenderingFrame(
renderTreeParameters.loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
- renderTreeParameters.needsRepaint
+ renderTreeParameters.needsRepaint,
+ renderTreeParameters.placementChanged
);
if (!renderTreeParameters.loaded) {
diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp
index 85e37a014a..5f04bfdfed 100644
--- a/src/mbgl/renderer/renderer_impl.hpp
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -19,7 +19,6 @@ class Renderer::Impl {
public:
Impl(gfx::RendererBackend&,
float pixelRatio_,
- optional<std::string> programCacheDir,
optional<std::string> localFontFamily_);
~Impl();
@@ -40,7 +39,6 @@ private:
RendererObserver* observer;
const float pixelRatio;
- const optional<std::string> programCacheDir;
std::unique_ptr<RenderStaticData> staticData;
enum class RenderState {
diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp
index e451dab6d3..035244e371 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -94,7 +94,7 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
const uint8_t maxZ = impl().getZoomRange().max;
for (const auto& pair : tilePyramid.getTiles()) {
if (pair.first.canonical.z <= maxZ) {
- static_cast<GeoJSONTile*>(pair.second.get())->updateData(data_->getTile(pair.first.canonical));
+ static_cast<GeoJSONTile*>(pair.second.get())->updateData(data_->getTile(pair.first.canonical), needsRelayout);
}
}
}
diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp
index 54e0b1eb26..7f0fad1500 100644
--- a/src/mbgl/renderer/tile_pyramid.cpp
+++ b/src/mbgl/renderer/tile_pyramid.cpp
@@ -221,8 +221,6 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
pair.second->setShowCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision);
}
- fadingTiles = false;
-
// Initialize renderable tiles and update the contained layer render data.
for (auto& entry : renderedTiles) {
Tile& tile = entry.second;
@@ -230,7 +228,6 @@ void TilePyramid::update(const std::vector<Immutable<style::LayerProperties>>& l
tile.usedByRenderedLayers = false;
const bool holdForFade = tile.holdForFade();
- fadingTiles = (fadingTiles || holdForFade);
for (const auto& layerProperties : layers) {
const auto* typeInfo = layerProperties->baseImpl->getTypeInfo();
if (holdForFade && typeInfo->fadingTiles == LayerTypeInfo::FadingTiles::NotRequired) {
@@ -374,6 +371,7 @@ void TilePyramid::dumpDebugLogs() const {
}
void TilePyramid::clearAll() {
+ fadingTiles = false;
tiles.clear();
renderedTiles.clear();
cache.clear();
@@ -385,9 +383,11 @@ void TilePyramid::addRenderTile(const UnwrappedTileID& tileID, Tile& tile) {
}
void TilePyramid::updateFadingTiles() {
+ fadingTiles = false;
for (auto& entry : renderedTiles) {
Tile& tile = entry.second;
if (tile.holdForFade()) {
+ fadingTiles = true;
tile.performedFadePlacement();
}
}
diff --git a/src/mbgl/style/conversion/constant.cpp b/src/mbgl/style/conversion/constant.cpp
index 0fcaab433b..ffdb17858d 100644
--- a/src/mbgl/style/conversion/constant.cpp
+++ b/src/mbgl/style/conversion/constant.cpp
@@ -86,6 +86,7 @@ template optional<TextTransformType> Converter<TextTransformType>::operator()(co
template optional<TranslateAnchorType> Converter<TranslateAnchorType>::operator()(const Convertible&, Error&) const;
template optional<VisibilityType> Converter<VisibilityType>::operator()(const Convertible&, Error&) const;
template optional<std::vector<TextVariableAnchorType>> Converter<std::vector<TextVariableAnchorType>>::operator()(const Convertible&, Error&) const;
+template optional<std::vector<TextWritingModeType>> Converter<std::vector<TextWritingModeType>>::operator()(const Convertible&, Error&) const;
optional<Color> Converter<Color>::operator()(const Convertible& value, Error& error) const {
optional<std::string> string = toString(value);
diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp
index 4e8d9c48e5..1b77985322 100644
--- a/src/mbgl/style/conversion/filter.cpp
+++ b/src/mbgl/style/conversion/filter.cpp
@@ -195,11 +195,16 @@ ParseResult convertLegacyFilter(const Convertible& values, Error& error) {
return {std::make_unique<Literal>(true)};
}
+ if (!isArray(values) || arrayLength(values) == 0) {
+ error.message = "filter value must be a non empty array";
+ return nullopt;
+ }
+
optional<std::string> op = toString(arrayMember(values, 0));
if (!op) {
error.message = "filter operator must be a string";
- return {};
+ return nullopt;
} else if (arrayLength(values) <= 1) {
return {std::make_unique<Literal>(*op != "any")};
} else {
diff --git a/src/mbgl/style/conversion/function.cpp b/src/mbgl/style/conversion/function.cpp
index 086bc4e277..646c1983a2 100644
--- a/src/mbgl/style/conversion/function.cpp
+++ b/src/mbgl/style/conversion/function.cpp
@@ -152,6 +152,8 @@ template optional<PropertyExpression<TranslateAnchorType>>
template optional<PropertyExpression<Formatted>>
convertFunctionToExpression<Formatted>(const Convertible&, Error&, bool);
+template optional<PropertyExpression<std::vector<TextWritingModeType>>>
+ convertFunctionToExpression<std::vector<TextWritingModeType>>(const Convertible&, Error&, bool);
// Ad-hoc Converters for double and int64_t. We should replace float with double wholesale,
// and promote the int64_t Converter to general use (and it should check that the input is
diff --git a/src/mbgl/style/conversion/geojson_options.cpp b/src/mbgl/style/conversion/geojson_options.cpp
index 11bd7cc507..08553e34bb 100644
--- a/src/mbgl/style/conversion/geojson_options.cpp
+++ b/src/mbgl/style/conversion/geojson_options.cpp
@@ -1,11 +1,15 @@
#include <mbgl/style/conversion/geojson_options.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/style/expression/dsl.hpp>
+
+#include <sstream>
namespace mbgl {
namespace style {
namespace conversion {
-optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value, Error& error) const {
+optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value,
+ Error& error) const {
GeoJSONOptions options;
const auto minzoomValue = objectMember(value, "minzoom");
@@ -83,12 +87,71 @@ optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible
if (toBool(*lineMetricsValue)) {
options.lineMetrics = *toBool(*lineMetricsValue);
} else {
- error = { "GeoJSON source lineMetrics value must be a boolean" };
+ error.message = "GeoJSON source lineMetrics value must be a boolean";
+ return nullopt;
+ }
+ }
+
+ const auto clusterProperties = objectMember(value, "clusterProperties");
+ if (clusterProperties) {
+ if (!isObject(*clusterProperties)) {
+ error.message = "GeoJSON source clusterProperties value must be an object";
+ return nullopt;
+ }
+ GeoJSONOptions::ClusterProperties result;
+ assert(error.message.empty());
+ eachMember(
+ *clusterProperties,
+ [&](const std::string& k,
+ const mbgl::style::conversion::Convertible& v) -> optional<conversion::Error> {
+ // Each property shall be formed as ["key" : [operator, [mapExpression]]]
+ // or ["key" : [[operator, ['accumulated'], ['get', key]], [mapExpression]]]
+ if (!isArray(v) || arrayLength(v) != 2) {
+ error.message =
+ "GeoJSON source clusterProperties member must be an array with length of 2";
+ return nullopt;
+ }
+ auto map = expression::dsl::createExpression(arrayMember(v, 1));
+ if (!map) {
+ error.message =
+ "Failed to convert GeoJSON source clusterProperties map expression";
+ return nullopt;
+ }
+ std::unique_ptr<expression::Expression> reduce;
+ if (isArray(arrayMember(v, 0))) {
+ reduce = expression::dsl::createExpression(arrayMember(v, 0));
+ } else {
+ auto reduceOp = toString(arrayMember(v, 0));
+ if (!reduceOp) {
+ error.message =
+ "GeoJSON source clusterProperties member must contain a valid operator";
+ return nullopt;
+ }
+ std::stringstream ss;
+ // Reformulate reduce expression to [operator, ['accumulated'], ['get', key]]
+ // The reason to create expression via parsing string instead of invoking function
+ // createCompoundExpression is due to expression type disunity can’t be resolved
+ // with current logic of createCompoundExpression
+ ss << std::string(R"([")") << *reduceOp
+ << std::string(R"(", ["accumulated"], ["get", ")") << k
+ << std::string(R"("]])");
+ reduce = expression::dsl::createExpression(ss.str().c_str());
+ }
+ if (!reduce) {
+ error.message =
+ "Failed to convert GeoJSON source clusterProperties reduce expression";
+ return nullopt;
+ }
+ result.emplace(k, std::make_pair(std::move(map), std::move(reduce)));
+ return nullopt;
+ });
+ if (!error.message.empty()) {
return nullopt;
}
+ options.clusterProperties = std::move(result);
}
- return { options };
+ return { std::move(options) };
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/property_value.cpp b/src/mbgl/style/conversion/property_value.cpp
index 6e1d747324..5c2f720a60 100644
--- a/src/mbgl/style/conversion/property_value.cpp
+++ b/src/mbgl/style/conversion/property_value.cpp
@@ -80,6 +80,7 @@ template optional<PropertyValue<TextJustifyType>> Converter<PropertyValue<TextJu
template optional<PropertyValue<TextTransformType>> Converter<PropertyValue<TextTransformType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const;
template optional<PropertyValue<TranslateAnchorType>> Converter<PropertyValue<TranslateAnchorType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const;
template optional<PropertyValue<mbgl::style::expression::Formatted>> Converter<PropertyValue<mbgl::style::expression::Formatted>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const;
+template optional<PropertyValue<std::vector<TextWritingModeType>>> Converter<PropertyValue<std::vector<TextWritingModeType>>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const;
} // namespace conversion
} // namespace style
diff --git a/src/mbgl/style/conversion/source.cpp b/src/mbgl/style/conversion/source.cpp
index 5ecbd3b474..de41adc89f 100644
--- a/src/mbgl/style/conversion/source.cpp
+++ b/src/mbgl/style/conversion/source.cpp
@@ -86,8 +86,25 @@ static optional<std::unique_ptr<Source>> convertVectorSource(const std::string&
if (!urlOrTileset) {
return nullopt;
}
-
- return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
+ auto maxzoomValue = objectMember(value, "maxzoom");
+ optional<float> maxzoom;
+ if (maxzoomValue) {
+ maxzoom = toNumber(*maxzoomValue);
+ if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
+ error.message = "invalid maxzoom";
+ return nullopt;
+ }
+ }
+ auto minzoomValue = objectMember(value, "minzoom");
+ optional<float> minzoom;
+ if (minzoomValue) {
+ minzoom = toNumber(*minzoomValue);
+ if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
+ error.message = "invalid minzoom";
+ return nullopt;
+ }
+ }
+ return {std::make_unique<VectorSource>(id, std::move(*urlOrTileset), std::move(maxzoom), std::move(minzoom))};
}
static optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
index 34f0f5dea3..c637856ad9 100644
--- a/src/mbgl/style/expression/compound_expression.cpp
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -368,6 +368,18 @@ const auto& lineProgressCompoundExpression() {
return signature;
}
+const auto& accumulatedCompoundExpression() {
+ const static auto signature = detail::makeSignature("accumulated", [](const EvaluationContext& params) -> Result<Value> {
+ if (!params.accumulated) {
+ return EvaluationError {
+ "The 'accumulated' expression is unavailable in the current evaluation context."
+ };
+ }
+ return Value(toExpressionValue(*params.accumulated));
+ });
+ return signature;
+}
+
const auto& hasContextCompoundExpression() {
static auto signature = detail::makeSignature("has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
if (!params.feature) {
@@ -871,6 +883,7 @@ MAPBOX_ETERNAL_CONSTEXPR const auto compoundExpressionRegistry = mapbox::eternal
{ "zoom", zoomCompoundExpression },
{ "heatmap-density", heatmapDensityCompoundExpression },
{ "line-progress", lineProgressCompoundExpression },
+ { "accumulated", accumulatedCompoundExpression },
{ "has", hasContextCompoundExpression },
{ "has", hasObjectCompoundExpression },
{ "get", getContextCompoundExpression },
diff --git a/src/mbgl/style/expression/dsl.cpp b/src/mbgl/style/expression/dsl.cpp
index 70442de968..fac7dfbbd9 100644
--- a/src/mbgl/style/expression/dsl.cpp
+++ b/src/mbgl/style/expression/dsl.cpp
@@ -10,6 +10,10 @@
#include <mbgl/style/expression/compound_expression.hpp>
#include <mbgl/style/expression/format_expression.hpp>
+#include <mapbox/geojsonvt.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <rapidjson/document.h>
+
namespace mbgl {
namespace style {
namespace expression {
@@ -21,6 +25,25 @@ std::unique_ptr<Expression> compound(const char* op, std::vector<std::unique_ptr
assert(result);
return std::move(*result);
}
+
+std::unique_ptr<Expression> createExpression(const char* expr) {
+ using JSValue = rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document;
+ document.Parse<0>(expr);
+ if (document.HasParseError()) return nullptr;
+
+ const JSValue* expression = &document;
+ expression::ParsingContext ctx;
+ expression::ParseResult parsed =
+ ctx.parseExpression(mbgl::style::conversion::Convertible(expression));
+ return parsed ? std::move(*parsed) : nullptr;
+}
+
+std::unique_ptr<Expression> createExpression(const mbgl::style::conversion::Convertible& expr) {
+ expression::ParsingContext ctx;
+ expression::ParseResult parsed = ctx.parseExpression(expr);
+ return parsed ? std::move(*parsed) : nullptr;
+}
std::unique_ptr<Expression> error(std::string message) {
return std::make_unique<Error>(std::move(message));
diff --git a/src/mbgl/style/expression/expression.cpp b/src/mbgl/style/expression/expression.cpp
index d61f435eec..6bfda99064 100644
--- a/src/mbgl/style/expression/expression.cpp
+++ b/src/mbgl/style/expression/expression.cpp
@@ -25,13 +25,17 @@ public:
return optional<mbgl::Value>();
}
};
-
-
+
EvaluationResult Expression::evaluate(optional<float> zoom, const Feature& feature, optional<double> colorRampParameter) const {
GeoJSONFeature f(feature);
return this->evaluate(EvaluationContext(zoom, &f, colorRampParameter));
}
+EvaluationResult Expression::evaluate(optional<mbgl::Value> accumulated, const Feature& feature) const {
+ GeoJSONFeature f(feature);
+ return this->evaluate(EvaluationContext(accumulated, &f));
+}
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp
index a7c04f563d..6ce3a9bfaa 100644
--- a/src/mbgl/style/expression/parsing_context.cpp
+++ b/src/mbgl/style/expression/parsing_context.cpp
@@ -73,7 +73,8 @@ bool isConstant(const Expression& expression) {
return isFeatureConstant(expression) &&
isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}}) &&
- isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "line-progress"}});
+ isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "line-progress"}}) &&
+ isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "accumulated"}});
}
using namespace mbgl::style::conversion;
diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp
index 7e11efaa09..6d18f1b9bd 100644
--- a/src/mbgl/style/expression/value.cpp
+++ b/src/mbgl/style/expression/value.cpp
@@ -374,6 +374,9 @@ template struct ValueConverter<HillshadeIlluminationAnchorType>;
template type::Type valueTypeToExpressionType<LightAnchorType>();
template struct ValueConverter<LightAnchorType>;
+template type::Type valueTypeToExpressionType<std::vector<TextWritingModeType>>();
+template struct ValueConverter<std::vector<TextWritingModeType>>;
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp
index 3c7a2a7569..1693c47204 100644
--- a/src/mbgl/style/layers/background_layer.cpp
+++ b/src/mbgl/style/layers/background_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -147,7 +148,7 @@ TransitionOptions BackgroundLayer::getBackgroundPatternTransition() const {
using namespace conversion;
optional<Error> BackgroundLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
BackgroundColor,
BackgroundOpacity,
BackgroundPattern,
@@ -157,12 +158,12 @@ optional<Error> BackgroundLayer::setPaintProperty(const std::string& name, const
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "background-color", static_cast<uint8_t>(Property::BackgroundColor) },
- { "background-opacity", static_cast<uint8_t>(Property::BackgroundOpacity) },
- { "background-pattern", static_cast<uint8_t>(Property::BackgroundPattern) },
- { "background-color-transition", static_cast<uint8_t>(Property::BackgroundColorTransition) },
- { "background-opacity-transition", static_cast<uint8_t>(Property::BackgroundOpacityTransition) },
- { "background-pattern-transition", static_cast<uint8_t>(Property::BackgroundPatternTransition) }
+ { "background-color", mbgl::underlying_type(Property::BackgroundColor) },
+ { "background-opacity", mbgl::underlying_type(Property::BackgroundOpacity) },
+ { "background-pattern", mbgl::underlying_type(Property::BackgroundPattern) },
+ { "background-color-transition", mbgl::underlying_type(Property::BackgroundColorTransition) },
+ { "background-opacity-transition", mbgl::underlying_type(Property::BackgroundOpacityTransition) },
+ { "background-pattern-transition", mbgl::underlying_type(Property::BackgroundPatternTransition) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp
index 8c3debd1ac..2293ed222e 100644
--- a/src/mbgl/style/layers/circle_layer.cpp
+++ b/src/mbgl/style/layers/circle_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -363,7 +364,7 @@ TransitionOptions CircleLayer::getCircleTranslateAnchorTransition() const {
using namespace conversion;
optional<Error> CircleLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
CircleBlur,
CircleColor,
CircleOpacity,
@@ -389,28 +390,28 @@ optional<Error> CircleLayer::setPaintProperty(const std::string& name, const Con
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "circle-blur", static_cast<uint8_t>(Property::CircleBlur) },
- { "circle-color", static_cast<uint8_t>(Property::CircleColor) },
- { "circle-opacity", static_cast<uint8_t>(Property::CircleOpacity) },
- { "circle-pitch-alignment", static_cast<uint8_t>(Property::CirclePitchAlignment) },
- { "circle-pitch-scale", static_cast<uint8_t>(Property::CirclePitchScale) },
- { "circle-radius", static_cast<uint8_t>(Property::CircleRadius) },
- { "circle-stroke-color", static_cast<uint8_t>(Property::CircleStrokeColor) },
- { "circle-stroke-opacity", static_cast<uint8_t>(Property::CircleStrokeOpacity) },
- { "circle-stroke-width", static_cast<uint8_t>(Property::CircleStrokeWidth) },
- { "circle-translate", static_cast<uint8_t>(Property::CircleTranslate) },
- { "circle-translate-anchor", static_cast<uint8_t>(Property::CircleTranslateAnchor) },
- { "circle-blur-transition", static_cast<uint8_t>(Property::CircleBlurTransition) },
- { "circle-color-transition", static_cast<uint8_t>(Property::CircleColorTransition) },
- { "circle-opacity-transition", static_cast<uint8_t>(Property::CircleOpacityTransition) },
- { "circle-pitch-alignment-transition", static_cast<uint8_t>(Property::CirclePitchAlignmentTransition) },
- { "circle-pitch-scale-transition", static_cast<uint8_t>(Property::CirclePitchScaleTransition) },
- { "circle-radius-transition", static_cast<uint8_t>(Property::CircleRadiusTransition) },
- { "circle-stroke-color-transition", static_cast<uint8_t>(Property::CircleStrokeColorTransition) },
- { "circle-stroke-opacity-transition", static_cast<uint8_t>(Property::CircleStrokeOpacityTransition) },
- { "circle-stroke-width-transition", static_cast<uint8_t>(Property::CircleStrokeWidthTransition) },
- { "circle-translate-transition", static_cast<uint8_t>(Property::CircleTranslateTransition) },
- { "circle-translate-anchor-transition", static_cast<uint8_t>(Property::CircleTranslateAnchorTransition) }
+ { "circle-blur", mbgl::underlying_type(Property::CircleBlur) },
+ { "circle-color", mbgl::underlying_type(Property::CircleColor) },
+ { "circle-opacity", mbgl::underlying_type(Property::CircleOpacity) },
+ { "circle-pitch-alignment", mbgl::underlying_type(Property::CirclePitchAlignment) },
+ { "circle-pitch-scale", mbgl::underlying_type(Property::CirclePitchScale) },
+ { "circle-radius", mbgl::underlying_type(Property::CircleRadius) },
+ { "circle-stroke-color", mbgl::underlying_type(Property::CircleStrokeColor) },
+ { "circle-stroke-opacity", mbgl::underlying_type(Property::CircleStrokeOpacity) },
+ { "circle-stroke-width", mbgl::underlying_type(Property::CircleStrokeWidth) },
+ { "circle-translate", mbgl::underlying_type(Property::CircleTranslate) },
+ { "circle-translate-anchor", mbgl::underlying_type(Property::CircleTranslateAnchor) },
+ { "circle-blur-transition", mbgl::underlying_type(Property::CircleBlurTransition) },
+ { "circle-color-transition", mbgl::underlying_type(Property::CircleColorTransition) },
+ { "circle-opacity-transition", mbgl::underlying_type(Property::CircleOpacityTransition) },
+ { "circle-pitch-alignment-transition", mbgl::underlying_type(Property::CirclePitchAlignmentTransition) },
+ { "circle-pitch-scale-transition", mbgl::underlying_type(Property::CirclePitchScaleTransition) },
+ { "circle-radius-transition", mbgl::underlying_type(Property::CircleRadiusTransition) },
+ { "circle-stroke-color-transition", mbgl::underlying_type(Property::CircleStrokeColorTransition) },
+ { "circle-stroke-opacity-transition", mbgl::underlying_type(Property::CircleStrokeOpacityTransition) },
+ { "circle-stroke-width-transition", mbgl::underlying_type(Property::CircleStrokeWidthTransition) },
+ { "circle-translate-transition", mbgl::underlying_type(Property::CircleTranslateTransition) },
+ { "circle-translate-anchor-transition", mbgl::underlying_type(Property::CircleTranslateAnchorTransition) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp
index 2c07cc56c5..50e32cf812 100644
--- a/src/mbgl/style/layers/fill_extrusion_layer.cpp
+++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -282,7 +283,7 @@ TransitionOptions FillExtrusionLayer::getFillExtrusionVerticalGradientTransition
using namespace conversion;
optional<Error> FillExtrusionLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
FillExtrusionBase,
FillExtrusionColor,
FillExtrusionHeight,
@@ -302,22 +303,22 @@ optional<Error> FillExtrusionLayer::setPaintProperty(const std::string& name, co
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "fill-extrusion-base", static_cast<uint8_t>(Property::FillExtrusionBase) },
- { "fill-extrusion-color", static_cast<uint8_t>(Property::FillExtrusionColor) },
- { "fill-extrusion-height", static_cast<uint8_t>(Property::FillExtrusionHeight) },
- { "fill-extrusion-opacity", static_cast<uint8_t>(Property::FillExtrusionOpacity) },
- { "fill-extrusion-pattern", static_cast<uint8_t>(Property::FillExtrusionPattern) },
- { "fill-extrusion-translate", static_cast<uint8_t>(Property::FillExtrusionTranslate) },
- { "fill-extrusion-translate-anchor", static_cast<uint8_t>(Property::FillExtrusionTranslateAnchor) },
- { "fill-extrusion-vertical-gradient", static_cast<uint8_t>(Property::FillExtrusionVerticalGradient) },
- { "fill-extrusion-base-transition", static_cast<uint8_t>(Property::FillExtrusionBaseTransition) },
- { "fill-extrusion-color-transition", static_cast<uint8_t>(Property::FillExtrusionColorTransition) },
- { "fill-extrusion-height-transition", static_cast<uint8_t>(Property::FillExtrusionHeightTransition) },
- { "fill-extrusion-opacity-transition", static_cast<uint8_t>(Property::FillExtrusionOpacityTransition) },
- { "fill-extrusion-pattern-transition", static_cast<uint8_t>(Property::FillExtrusionPatternTransition) },
- { "fill-extrusion-translate-transition", static_cast<uint8_t>(Property::FillExtrusionTranslateTransition) },
- { "fill-extrusion-translate-anchor-transition", static_cast<uint8_t>(Property::FillExtrusionTranslateAnchorTransition) },
- { "fill-extrusion-vertical-gradient-transition", static_cast<uint8_t>(Property::FillExtrusionVerticalGradientTransition) }
+ { "fill-extrusion-base", mbgl::underlying_type(Property::FillExtrusionBase) },
+ { "fill-extrusion-color", mbgl::underlying_type(Property::FillExtrusionColor) },
+ { "fill-extrusion-height", mbgl::underlying_type(Property::FillExtrusionHeight) },
+ { "fill-extrusion-opacity", mbgl::underlying_type(Property::FillExtrusionOpacity) },
+ { "fill-extrusion-pattern", mbgl::underlying_type(Property::FillExtrusionPattern) },
+ { "fill-extrusion-translate", mbgl::underlying_type(Property::FillExtrusionTranslate) },
+ { "fill-extrusion-translate-anchor", mbgl::underlying_type(Property::FillExtrusionTranslateAnchor) },
+ { "fill-extrusion-vertical-gradient", mbgl::underlying_type(Property::FillExtrusionVerticalGradient) },
+ { "fill-extrusion-base-transition", mbgl::underlying_type(Property::FillExtrusionBaseTransition) },
+ { "fill-extrusion-color-transition", mbgl::underlying_type(Property::FillExtrusionColorTransition) },
+ { "fill-extrusion-height-transition", mbgl::underlying_type(Property::FillExtrusionHeightTransition) },
+ { "fill-extrusion-opacity-transition", mbgl::underlying_type(Property::FillExtrusionOpacityTransition) },
+ { "fill-extrusion-pattern-transition", mbgl::underlying_type(Property::FillExtrusionPatternTransition) },
+ { "fill-extrusion-translate-transition", mbgl::underlying_type(Property::FillExtrusionTranslateTransition) },
+ { "fill-extrusion-translate-anchor-transition", mbgl::underlying_type(Property::FillExtrusionTranslateAnchorTransition) },
+ { "fill-extrusion-vertical-gradient-transition", mbgl::underlying_type(Property::FillExtrusionVerticalGradientTransition) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp
index 0aa19b834a..e08c8f3962 100644
--- a/src/mbgl/style/layers/fill_layer.cpp
+++ b/src/mbgl/style/layers/fill_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -255,7 +256,7 @@ TransitionOptions FillLayer::getFillTranslateAnchorTransition() const {
using namespace conversion;
optional<Error> FillLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
FillAntialias,
FillColor,
FillOpacity,
@@ -273,20 +274,20 @@ optional<Error> FillLayer::setPaintProperty(const std::string& name, const Conve
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "fill-antialias", static_cast<uint8_t>(Property::FillAntialias) },
- { "fill-color", static_cast<uint8_t>(Property::FillColor) },
- { "fill-opacity", static_cast<uint8_t>(Property::FillOpacity) },
- { "fill-outline-color", static_cast<uint8_t>(Property::FillOutlineColor) },
- { "fill-pattern", static_cast<uint8_t>(Property::FillPattern) },
- { "fill-translate", static_cast<uint8_t>(Property::FillTranslate) },
- { "fill-translate-anchor", static_cast<uint8_t>(Property::FillTranslateAnchor) },
- { "fill-antialias-transition", static_cast<uint8_t>(Property::FillAntialiasTransition) },
- { "fill-color-transition", static_cast<uint8_t>(Property::FillColorTransition) },
- { "fill-opacity-transition", static_cast<uint8_t>(Property::FillOpacityTransition) },
- { "fill-outline-color-transition", static_cast<uint8_t>(Property::FillOutlineColorTransition) },
- { "fill-pattern-transition", static_cast<uint8_t>(Property::FillPatternTransition) },
- { "fill-translate-transition", static_cast<uint8_t>(Property::FillTranslateTransition) },
- { "fill-translate-anchor-transition", static_cast<uint8_t>(Property::FillTranslateAnchorTransition) }
+ { "fill-antialias", mbgl::underlying_type(Property::FillAntialias) },
+ { "fill-color", mbgl::underlying_type(Property::FillColor) },
+ { "fill-opacity", mbgl::underlying_type(Property::FillOpacity) },
+ { "fill-outline-color", mbgl::underlying_type(Property::FillOutlineColor) },
+ { "fill-pattern", mbgl::underlying_type(Property::FillPattern) },
+ { "fill-translate", mbgl::underlying_type(Property::FillTranslate) },
+ { "fill-translate-anchor", mbgl::underlying_type(Property::FillTranslateAnchor) },
+ { "fill-antialias-transition", mbgl::underlying_type(Property::FillAntialiasTransition) },
+ { "fill-color-transition", mbgl::underlying_type(Property::FillColorTransition) },
+ { "fill-opacity-transition", mbgl::underlying_type(Property::FillOpacityTransition) },
+ { "fill-outline-color-transition", mbgl::underlying_type(Property::FillOutlineColorTransition) },
+ { "fill-pattern-transition", mbgl::underlying_type(Property::FillPatternTransition) },
+ { "fill-translate-transition", mbgl::underlying_type(Property::FillTranslateTransition) },
+ { "fill-translate-anchor-transition", mbgl::underlying_type(Property::FillTranslateAnchorTransition) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/fill_layer_impl.cpp b/src/mbgl/style/layers/fill_layer_impl.cpp
index 4df32e02fa..45e350e00e 100644
--- a/src/mbgl/style/layers/fill_layer_impl.cpp
+++ b/src/mbgl/style/layers/fill_layer_impl.cpp
@@ -8,6 +8,7 @@ bool FillLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const {
const auto& impl = static_cast<const style::FillLayer::Impl&>(other);
return filter != impl.filter ||
visibility != impl.visibility ||
+ paint.get<FillPattern>().value != impl.paint.get<FillPattern>().value ||
paint.hasDataDrivenPropertyDifference(impl.paint);
}
diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp
index d35cb10b78..cb99f76a51 100644
--- a/src/mbgl/style/layers/heatmap_layer.cpp
+++ b/src/mbgl/style/layers/heatmap_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -203,7 +204,7 @@ TransitionOptions HeatmapLayer::getHeatmapWeightTransition() const {
using namespace conversion;
optional<Error> HeatmapLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
HeatmapColor,
HeatmapIntensity,
HeatmapOpacity,
@@ -217,16 +218,16 @@ optional<Error> HeatmapLayer::setPaintProperty(const std::string& name, const Co
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "heatmap-color", static_cast<uint8_t>(Property::HeatmapColor) },
- { "heatmap-intensity", static_cast<uint8_t>(Property::HeatmapIntensity) },
- { "heatmap-opacity", static_cast<uint8_t>(Property::HeatmapOpacity) },
- { "heatmap-radius", static_cast<uint8_t>(Property::HeatmapRadius) },
- { "heatmap-weight", static_cast<uint8_t>(Property::HeatmapWeight) },
- { "heatmap-color-transition", static_cast<uint8_t>(Property::HeatmapColorTransition) },
- { "heatmap-intensity-transition", static_cast<uint8_t>(Property::HeatmapIntensityTransition) },
- { "heatmap-opacity-transition", static_cast<uint8_t>(Property::HeatmapOpacityTransition) },
- { "heatmap-radius-transition", static_cast<uint8_t>(Property::HeatmapRadiusTransition) },
- { "heatmap-weight-transition", static_cast<uint8_t>(Property::HeatmapWeightTransition) }
+ { "heatmap-color", mbgl::underlying_type(Property::HeatmapColor) },
+ { "heatmap-intensity", mbgl::underlying_type(Property::HeatmapIntensity) },
+ { "heatmap-opacity", mbgl::underlying_type(Property::HeatmapOpacity) },
+ { "heatmap-radius", mbgl::underlying_type(Property::HeatmapRadius) },
+ { "heatmap-weight", mbgl::underlying_type(Property::HeatmapWeight) },
+ { "heatmap-color-transition", mbgl::underlying_type(Property::HeatmapColorTransition) },
+ { "heatmap-intensity-transition", mbgl::underlying_type(Property::HeatmapIntensityTransition) },
+ { "heatmap-opacity-transition", mbgl::underlying_type(Property::HeatmapOpacityTransition) },
+ { "heatmap-radius-transition", mbgl::underlying_type(Property::HeatmapRadiusTransition) },
+ { "heatmap-weight-transition", mbgl::underlying_type(Property::HeatmapWeightTransition) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/hillshade_layer.cpp b/src/mbgl/style/layers/hillshade_layer.cpp
index ba22ab25ab..95a395ef25 100644
--- a/src/mbgl/style/layers/hillshade_layer.cpp
+++ b/src/mbgl/style/layers/hillshade_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -228,7 +229,7 @@ TransitionOptions HillshadeLayer::getHillshadeShadowColorTransition() const {
using namespace conversion;
optional<Error> HillshadeLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
HillshadeAccentColor,
HillshadeExaggeration,
HillshadeHighlightColor,
@@ -244,18 +245,18 @@ optional<Error> HillshadeLayer::setPaintProperty(const std::string& name, const
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "hillshade-accent-color", static_cast<uint8_t>(Property::HillshadeAccentColor) },
- { "hillshade-exaggeration", static_cast<uint8_t>(Property::HillshadeExaggeration) },
- { "hillshade-highlight-color", static_cast<uint8_t>(Property::HillshadeHighlightColor) },
- { "hillshade-illumination-anchor", static_cast<uint8_t>(Property::HillshadeIlluminationAnchor) },
- { "hillshade-illumination-direction", static_cast<uint8_t>(Property::HillshadeIlluminationDirection) },
- { "hillshade-shadow-color", static_cast<uint8_t>(Property::HillshadeShadowColor) },
- { "hillshade-accent-color-transition", static_cast<uint8_t>(Property::HillshadeAccentColorTransition) },
- { "hillshade-exaggeration-transition", static_cast<uint8_t>(Property::HillshadeExaggerationTransition) },
- { "hillshade-highlight-color-transition", static_cast<uint8_t>(Property::HillshadeHighlightColorTransition) },
- { "hillshade-illumination-anchor-transition", static_cast<uint8_t>(Property::HillshadeIlluminationAnchorTransition) },
- { "hillshade-illumination-direction-transition", static_cast<uint8_t>(Property::HillshadeIlluminationDirectionTransition) },
- { "hillshade-shadow-color-transition", static_cast<uint8_t>(Property::HillshadeShadowColorTransition) }
+ { "hillshade-accent-color", mbgl::underlying_type(Property::HillshadeAccentColor) },
+ { "hillshade-exaggeration", mbgl::underlying_type(Property::HillshadeExaggeration) },
+ { "hillshade-highlight-color", mbgl::underlying_type(Property::HillshadeHighlightColor) },
+ { "hillshade-illumination-anchor", mbgl::underlying_type(Property::HillshadeIlluminationAnchor) },
+ { "hillshade-illumination-direction", mbgl::underlying_type(Property::HillshadeIlluminationDirection) },
+ { "hillshade-shadow-color", mbgl::underlying_type(Property::HillshadeShadowColor) },
+ { "hillshade-accent-color-transition", mbgl::underlying_type(Property::HillshadeAccentColorTransition) },
+ { "hillshade-exaggeration-transition", mbgl::underlying_type(Property::HillshadeExaggerationTransition) },
+ { "hillshade-highlight-color-transition", mbgl::underlying_type(Property::HillshadeHighlightColorTransition) },
+ { "hillshade-illumination-anchor-transition", mbgl::underlying_type(Property::HillshadeIlluminationAnchorTransition) },
+ { "hillshade-illumination-direction-transition", mbgl::underlying_type(Property::HillshadeIlluminationDirectionTransition) },
+ { "hillshade-shadow-color-transition", mbgl::underlying_type(Property::HillshadeShadowColorTransition) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs
index 189857434f..da34565461 100644
--- a/src/mbgl/style/layers/layer.cpp.ejs
+++ b/src/mbgl/style/layers/layer.cpp.ejs
@@ -14,6 +14,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -176,7 +177,7 @@ TransitionOptions <%- camelize(type) %>Layer::get<%- camelize(property.name) %>T
using namespace conversion;
optional<Error> <%- camelize(type) %>Layer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
<% for (const property of paintProperties) { -%>
<%- camelize(property.name) %>,
<% } -%>
@@ -186,8 +187,8 @@ optional<Error> <%- camelize(type) %>Layer::setPaintProperty(const std::string&
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- <%- paintProperties.map(p => `{ "${p.name}", static_cast<uint8_t>(Property::${camelize(p.name)}) }`).join(',\n ') %>,
- <%- paintProperties.map(p => `{ "${p.name}-transition", static_cast<uint8_t>(Property::${camelize(p.name)}Transition) }`).join(',\n ') %>
+ <%- paintProperties.map(p => `{ "${p.name}", mbgl::underlying_type(Property::${camelize(p.name)}) }`).join(',\n ') %>,
+ <%- paintProperties.map(p => `{ "${p.name}-transition", mbgl::underlying_type(Property::${camelize(p.name)}Transition) }`).join(',\n ') %>
});
const auto it = properties.find(name.c_str());
@@ -254,7 +255,7 @@ optional<Error> <%- camelize(type) %>Layer::setLayoutProperty(const std::string&
<% } -%>
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- <%- layoutProperties.map(p => `{ "${p.name}", static_cast<uint8_t>(Property::${camelize(p.name)}) }`).join(',\n ') %>
+ <%- layoutProperties.map(p => `{ "${p.name}", mbgl::underlying_type(Property::${camelize(p.name)}) }`).join(',\n ') %>
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp
index 878816f6e4..deb85cad66 100644
--- a/src/mbgl/style/layers/line_layer.cpp
+++ b/src/mbgl/style/layers/line_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -429,7 +430,7 @@ TransitionOptions LineLayer::getLineWidthTransition() const {
using namespace conversion;
optional<Error> LineLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
LineBlur,
LineColor,
LineDasharray,
@@ -455,28 +456,28 @@ optional<Error> LineLayer::setPaintProperty(const std::string& name, const Conve
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "line-blur", static_cast<uint8_t>(Property::LineBlur) },
- { "line-color", static_cast<uint8_t>(Property::LineColor) },
- { "line-dasharray", static_cast<uint8_t>(Property::LineDasharray) },
- { "line-gap-width", static_cast<uint8_t>(Property::LineGapWidth) },
- { "line-gradient", static_cast<uint8_t>(Property::LineGradient) },
- { "line-offset", static_cast<uint8_t>(Property::LineOffset) },
- { "line-opacity", static_cast<uint8_t>(Property::LineOpacity) },
- { "line-pattern", static_cast<uint8_t>(Property::LinePattern) },
- { "line-translate", static_cast<uint8_t>(Property::LineTranslate) },
- { "line-translate-anchor", static_cast<uint8_t>(Property::LineTranslateAnchor) },
- { "line-width", static_cast<uint8_t>(Property::LineWidth) },
- { "line-blur-transition", static_cast<uint8_t>(Property::LineBlurTransition) },
- { "line-color-transition", static_cast<uint8_t>(Property::LineColorTransition) },
- { "line-dasharray-transition", static_cast<uint8_t>(Property::LineDasharrayTransition) },
- { "line-gap-width-transition", static_cast<uint8_t>(Property::LineGapWidthTransition) },
- { "line-gradient-transition", static_cast<uint8_t>(Property::LineGradientTransition) },
- { "line-offset-transition", static_cast<uint8_t>(Property::LineOffsetTransition) },
- { "line-opacity-transition", static_cast<uint8_t>(Property::LineOpacityTransition) },
- { "line-pattern-transition", static_cast<uint8_t>(Property::LinePatternTransition) },
- { "line-translate-transition", static_cast<uint8_t>(Property::LineTranslateTransition) },
- { "line-translate-anchor-transition", static_cast<uint8_t>(Property::LineTranslateAnchorTransition) },
- { "line-width-transition", static_cast<uint8_t>(Property::LineWidthTransition) }
+ { "line-blur", mbgl::underlying_type(Property::LineBlur) },
+ { "line-color", mbgl::underlying_type(Property::LineColor) },
+ { "line-dasharray", mbgl::underlying_type(Property::LineDasharray) },
+ { "line-gap-width", mbgl::underlying_type(Property::LineGapWidth) },
+ { "line-gradient", mbgl::underlying_type(Property::LineGradient) },
+ { "line-offset", mbgl::underlying_type(Property::LineOffset) },
+ { "line-opacity", mbgl::underlying_type(Property::LineOpacity) },
+ { "line-pattern", mbgl::underlying_type(Property::LinePattern) },
+ { "line-translate", mbgl::underlying_type(Property::LineTranslate) },
+ { "line-translate-anchor", mbgl::underlying_type(Property::LineTranslateAnchor) },
+ { "line-width", mbgl::underlying_type(Property::LineWidth) },
+ { "line-blur-transition", mbgl::underlying_type(Property::LineBlurTransition) },
+ { "line-color-transition", mbgl::underlying_type(Property::LineColorTransition) },
+ { "line-dasharray-transition", mbgl::underlying_type(Property::LineDasharrayTransition) },
+ { "line-gap-width-transition", mbgl::underlying_type(Property::LineGapWidthTransition) },
+ { "line-gradient-transition", mbgl::underlying_type(Property::LineGradientTransition) },
+ { "line-offset-transition", mbgl::underlying_type(Property::LineOffsetTransition) },
+ { "line-opacity-transition", mbgl::underlying_type(Property::LineOpacityTransition) },
+ { "line-pattern-transition", mbgl::underlying_type(Property::LinePatternTransition) },
+ { "line-translate-transition", mbgl::underlying_type(Property::LineTranslateTransition) },
+ { "line-translate-anchor-transition", mbgl::underlying_type(Property::LineTranslateAnchorTransition) },
+ { "line-width-transition", mbgl::underlying_type(Property::LineWidthTransition) }
});
const auto it = properties.find(name.c_str());
@@ -670,10 +671,10 @@ optional<Error> LineLayer::setLayoutProperty(const std::string& name, const Conv
LineRoundLimit,
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "line-cap", static_cast<uint8_t>(Property::LineCap) },
- { "line-join", static_cast<uint8_t>(Property::LineJoin) },
- { "line-miter-limit", static_cast<uint8_t>(Property::LineMiterLimit) },
- { "line-round-limit", static_cast<uint8_t>(Property::LineRoundLimit) }
+ { "line-cap", mbgl::underlying_type(Property::LineCap) },
+ { "line-join", mbgl::underlying_type(Property::LineJoin) },
+ { "line-miter-limit", mbgl::underlying_type(Property::LineMiterLimit) },
+ { "line-round-limit", mbgl::underlying_type(Property::LineRoundLimit) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp
index 1e2cd748c1..ead2223ea6 100644
--- a/src/mbgl/style/layers/raster_layer.cpp
+++ b/src/mbgl/style/layers/raster_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -282,7 +283,7 @@ TransitionOptions RasterLayer::getRasterSaturationTransition() const {
using namespace conversion;
optional<Error> RasterLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
RasterBrightnessMax,
RasterBrightnessMin,
RasterContrast,
@@ -302,22 +303,22 @@ optional<Error> RasterLayer::setPaintProperty(const std::string& name, const Con
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "raster-brightness-max", static_cast<uint8_t>(Property::RasterBrightnessMax) },
- { "raster-brightness-min", static_cast<uint8_t>(Property::RasterBrightnessMin) },
- { "raster-contrast", static_cast<uint8_t>(Property::RasterContrast) },
- { "raster-fade-duration", static_cast<uint8_t>(Property::RasterFadeDuration) },
- { "raster-hue-rotate", static_cast<uint8_t>(Property::RasterHueRotate) },
- { "raster-opacity", static_cast<uint8_t>(Property::RasterOpacity) },
- { "raster-resampling", static_cast<uint8_t>(Property::RasterResampling) },
- { "raster-saturation", static_cast<uint8_t>(Property::RasterSaturation) },
- { "raster-brightness-max-transition", static_cast<uint8_t>(Property::RasterBrightnessMaxTransition) },
- { "raster-brightness-min-transition", static_cast<uint8_t>(Property::RasterBrightnessMinTransition) },
- { "raster-contrast-transition", static_cast<uint8_t>(Property::RasterContrastTransition) },
- { "raster-fade-duration-transition", static_cast<uint8_t>(Property::RasterFadeDurationTransition) },
- { "raster-hue-rotate-transition", static_cast<uint8_t>(Property::RasterHueRotateTransition) },
- { "raster-opacity-transition", static_cast<uint8_t>(Property::RasterOpacityTransition) },
- { "raster-resampling-transition", static_cast<uint8_t>(Property::RasterResamplingTransition) },
- { "raster-saturation-transition", static_cast<uint8_t>(Property::RasterSaturationTransition) }
+ { "raster-brightness-max", mbgl::underlying_type(Property::RasterBrightnessMax) },
+ { "raster-brightness-min", mbgl::underlying_type(Property::RasterBrightnessMin) },
+ { "raster-contrast", mbgl::underlying_type(Property::RasterContrast) },
+ { "raster-fade-duration", mbgl::underlying_type(Property::RasterFadeDuration) },
+ { "raster-hue-rotate", mbgl::underlying_type(Property::RasterHueRotate) },
+ { "raster-opacity", mbgl::underlying_type(Property::RasterOpacity) },
+ { "raster-resampling", mbgl::underlying_type(Property::RasterResampling) },
+ { "raster-saturation", mbgl::underlying_type(Property::RasterSaturation) },
+ { "raster-brightness-max-transition", mbgl::underlying_type(Property::RasterBrightnessMaxTransition) },
+ { "raster-brightness-min-transition", mbgl::underlying_type(Property::RasterBrightnessMinTransition) },
+ { "raster-contrast-transition", mbgl::underlying_type(Property::RasterContrastTransition) },
+ { "raster-fade-duration-transition", mbgl::underlying_type(Property::RasterFadeDurationTransition) },
+ { "raster-hue-rotate-transition", mbgl::underlying_type(Property::RasterHueRotateTransition) },
+ { "raster-opacity-transition", mbgl::underlying_type(Property::RasterOpacityTransition) },
+ { "raster-resampling-transition", mbgl::underlying_type(Property::RasterResamplingTransition) },
+ { "raster-saturation-transition", mbgl::underlying_type(Property::RasterSaturationTransition) }
});
const auto it = properties.find(name.c_str());
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 6748fbd4aa..157d8c745f 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/conversion/transition_options.hpp>
#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
#include <mapbox/eternal.hpp>
@@ -701,6 +702,22 @@ void SymbolLayer::setTextVariableAnchor(const PropertyValue<std::vector<TextVari
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
+PropertyValue<std::vector<TextWritingModeType>> SymbolLayer::getDefaultTextWritingMode() {
+ return TextWritingMode::defaultValue();
+}
+
+const PropertyValue<std::vector<TextWritingModeType>>& SymbolLayer::getTextWritingMode() const {
+ return impl().layout.get<TextWritingMode>();
+}
+
+void SymbolLayer::setTextWritingMode(const PropertyValue<std::vector<TextWritingModeType>>& value) {
+ if (value == getTextWritingMode())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->layout.get<TextWritingMode>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
// Paint properties
@@ -1085,7 +1102,7 @@ TransitionOptions SymbolLayer::getTextTranslateAnchorTransition() const {
using namespace conversion;
optional<Error> SymbolLayer::setPaintProperty(const std::string& name, const Convertible& value) {
- enum class Property : uint8_t {
+ enum class Property {
IconColor,
IconHaloBlur,
IconHaloColor,
@@ -1117,34 +1134,34 @@ optional<Error> SymbolLayer::setPaintProperty(const std::string& name, const Con
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "icon-color", static_cast<uint8_t>(Property::IconColor) },
- { "icon-halo-blur", static_cast<uint8_t>(Property::IconHaloBlur) },
- { "icon-halo-color", static_cast<uint8_t>(Property::IconHaloColor) },
- { "icon-halo-width", static_cast<uint8_t>(Property::IconHaloWidth) },
- { "icon-opacity", static_cast<uint8_t>(Property::IconOpacity) },
- { "icon-translate", static_cast<uint8_t>(Property::IconTranslate) },
- { "icon-translate-anchor", static_cast<uint8_t>(Property::IconTranslateAnchor) },
- { "text-color", static_cast<uint8_t>(Property::TextColor) },
- { "text-halo-blur", static_cast<uint8_t>(Property::TextHaloBlur) },
- { "text-halo-color", static_cast<uint8_t>(Property::TextHaloColor) },
- { "text-halo-width", static_cast<uint8_t>(Property::TextHaloWidth) },
- { "text-opacity", static_cast<uint8_t>(Property::TextOpacity) },
- { "text-translate", static_cast<uint8_t>(Property::TextTranslate) },
- { "text-translate-anchor", static_cast<uint8_t>(Property::TextTranslateAnchor) },
- { "icon-color-transition", static_cast<uint8_t>(Property::IconColorTransition) },
- { "icon-halo-blur-transition", static_cast<uint8_t>(Property::IconHaloBlurTransition) },
- { "icon-halo-color-transition", static_cast<uint8_t>(Property::IconHaloColorTransition) },
- { "icon-halo-width-transition", static_cast<uint8_t>(Property::IconHaloWidthTransition) },
- { "icon-opacity-transition", static_cast<uint8_t>(Property::IconOpacityTransition) },
- { "icon-translate-transition", static_cast<uint8_t>(Property::IconTranslateTransition) },
- { "icon-translate-anchor-transition", static_cast<uint8_t>(Property::IconTranslateAnchorTransition) },
- { "text-color-transition", static_cast<uint8_t>(Property::TextColorTransition) },
- { "text-halo-blur-transition", static_cast<uint8_t>(Property::TextHaloBlurTransition) },
- { "text-halo-color-transition", static_cast<uint8_t>(Property::TextHaloColorTransition) },
- { "text-halo-width-transition", static_cast<uint8_t>(Property::TextHaloWidthTransition) },
- { "text-opacity-transition", static_cast<uint8_t>(Property::TextOpacityTransition) },
- { "text-translate-transition", static_cast<uint8_t>(Property::TextTranslateTransition) },
- { "text-translate-anchor-transition", static_cast<uint8_t>(Property::TextTranslateAnchorTransition) }
+ { "icon-color", mbgl::underlying_type(Property::IconColor) },
+ { "icon-halo-blur", mbgl::underlying_type(Property::IconHaloBlur) },
+ { "icon-halo-color", mbgl::underlying_type(Property::IconHaloColor) },
+ { "icon-halo-width", mbgl::underlying_type(Property::IconHaloWidth) },
+ { "icon-opacity", mbgl::underlying_type(Property::IconOpacity) },
+ { "icon-translate", mbgl::underlying_type(Property::IconTranslate) },
+ { "icon-translate-anchor", mbgl::underlying_type(Property::IconTranslateAnchor) },
+ { "text-color", mbgl::underlying_type(Property::TextColor) },
+ { "text-halo-blur", mbgl::underlying_type(Property::TextHaloBlur) },
+ { "text-halo-color", mbgl::underlying_type(Property::TextHaloColor) },
+ { "text-halo-width", mbgl::underlying_type(Property::TextHaloWidth) },
+ { "text-opacity", mbgl::underlying_type(Property::TextOpacity) },
+ { "text-translate", mbgl::underlying_type(Property::TextTranslate) },
+ { "text-translate-anchor", mbgl::underlying_type(Property::TextTranslateAnchor) },
+ { "icon-color-transition", mbgl::underlying_type(Property::IconColorTransition) },
+ { "icon-halo-blur-transition", mbgl::underlying_type(Property::IconHaloBlurTransition) },
+ { "icon-halo-color-transition", mbgl::underlying_type(Property::IconHaloColorTransition) },
+ { "icon-halo-width-transition", mbgl::underlying_type(Property::IconHaloWidthTransition) },
+ { "icon-opacity-transition", mbgl::underlying_type(Property::IconOpacityTransition) },
+ { "icon-translate-transition", mbgl::underlying_type(Property::IconTranslateTransition) },
+ { "icon-translate-anchor-transition", mbgl::underlying_type(Property::IconTranslateAnchorTransition) },
+ { "text-color-transition", mbgl::underlying_type(Property::TextColorTransition) },
+ { "text-halo-blur-transition", mbgl::underlying_type(Property::TextHaloBlurTransition) },
+ { "text-halo-color-transition", mbgl::underlying_type(Property::TextHaloColorTransition) },
+ { "text-halo-width-transition", mbgl::underlying_type(Property::TextHaloWidthTransition) },
+ { "text-opacity-transition", mbgl::underlying_type(Property::TextOpacityTransition) },
+ { "text-translate-transition", mbgl::underlying_type(Property::TextTranslateTransition) },
+ { "text-translate-anchor-transition", mbgl::underlying_type(Property::TextTranslateAnchorTransition) }
});
const auto it = properties.find(name.c_str());
@@ -1387,48 +1404,50 @@ optional<Error> SymbolLayer::setLayoutProperty(const std::string& name, const Co
TextSize,
TextTransform,
TextVariableAnchor,
+ TextWritingMode,
};
MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
- { "icon-allow-overlap", static_cast<uint8_t>(Property::IconAllowOverlap) },
- { "icon-anchor", static_cast<uint8_t>(Property::IconAnchor) },
- { "icon-ignore-placement", static_cast<uint8_t>(Property::IconIgnorePlacement) },
- { "icon-image", static_cast<uint8_t>(Property::IconImage) },
- { "icon-keep-upright", static_cast<uint8_t>(Property::IconKeepUpright) },
- { "icon-offset", static_cast<uint8_t>(Property::IconOffset) },
- { "icon-optional", static_cast<uint8_t>(Property::IconOptional) },
- { "icon-padding", static_cast<uint8_t>(Property::IconPadding) },
- { "icon-pitch-alignment", static_cast<uint8_t>(Property::IconPitchAlignment) },
- { "icon-rotate", static_cast<uint8_t>(Property::IconRotate) },
- { "icon-rotation-alignment", static_cast<uint8_t>(Property::IconRotationAlignment) },
- { "icon-size", static_cast<uint8_t>(Property::IconSize) },
- { "icon-text-fit", static_cast<uint8_t>(Property::IconTextFit) },
- { "icon-text-fit-padding", static_cast<uint8_t>(Property::IconTextFitPadding) },
- { "symbol-avoid-edges", static_cast<uint8_t>(Property::SymbolAvoidEdges) },
- { "symbol-placement", static_cast<uint8_t>(Property::SymbolPlacement) },
- { "symbol-sort-key", static_cast<uint8_t>(Property::SymbolSortKey) },
- { "symbol-spacing", static_cast<uint8_t>(Property::SymbolSpacing) },
- { "symbol-z-order", static_cast<uint8_t>(Property::SymbolZOrder) },
- { "text-allow-overlap", static_cast<uint8_t>(Property::TextAllowOverlap) },
- { "text-anchor", static_cast<uint8_t>(Property::TextAnchor) },
- { "text-field", static_cast<uint8_t>(Property::TextField) },
- { "text-font", static_cast<uint8_t>(Property::TextFont) },
- { "text-ignore-placement", static_cast<uint8_t>(Property::TextIgnorePlacement) },
- { "text-justify", static_cast<uint8_t>(Property::TextJustify) },
- { "text-keep-upright", static_cast<uint8_t>(Property::TextKeepUpright) },
- { "text-letter-spacing", static_cast<uint8_t>(Property::TextLetterSpacing) },
- { "text-line-height", static_cast<uint8_t>(Property::TextLineHeight) },
- { "text-max-angle", static_cast<uint8_t>(Property::TextMaxAngle) },
- { "text-max-width", static_cast<uint8_t>(Property::TextMaxWidth) },
- { "text-offset", static_cast<uint8_t>(Property::TextOffset) },
- { "text-optional", static_cast<uint8_t>(Property::TextOptional) },
- { "text-padding", static_cast<uint8_t>(Property::TextPadding) },
- { "text-pitch-alignment", static_cast<uint8_t>(Property::TextPitchAlignment) },
- { "text-radial-offset", static_cast<uint8_t>(Property::TextRadialOffset) },
- { "text-rotate", static_cast<uint8_t>(Property::TextRotate) },
- { "text-rotation-alignment", static_cast<uint8_t>(Property::TextRotationAlignment) },
- { "text-size", static_cast<uint8_t>(Property::TextSize) },
- { "text-transform", static_cast<uint8_t>(Property::TextTransform) },
- { "text-variable-anchor", static_cast<uint8_t>(Property::TextVariableAnchor) }
+ { "icon-allow-overlap", mbgl::underlying_type(Property::IconAllowOverlap) },
+ { "icon-anchor", mbgl::underlying_type(Property::IconAnchor) },
+ { "icon-ignore-placement", mbgl::underlying_type(Property::IconIgnorePlacement) },
+ { "icon-image", mbgl::underlying_type(Property::IconImage) },
+ { "icon-keep-upright", mbgl::underlying_type(Property::IconKeepUpright) },
+ { "icon-offset", mbgl::underlying_type(Property::IconOffset) },
+ { "icon-optional", mbgl::underlying_type(Property::IconOptional) },
+ { "icon-padding", mbgl::underlying_type(Property::IconPadding) },
+ { "icon-pitch-alignment", mbgl::underlying_type(Property::IconPitchAlignment) },
+ { "icon-rotate", mbgl::underlying_type(Property::IconRotate) },
+ { "icon-rotation-alignment", mbgl::underlying_type(Property::IconRotationAlignment) },
+ { "icon-size", mbgl::underlying_type(Property::IconSize) },
+ { "icon-text-fit", mbgl::underlying_type(Property::IconTextFit) },
+ { "icon-text-fit-padding", mbgl::underlying_type(Property::IconTextFitPadding) },
+ { "symbol-avoid-edges", mbgl::underlying_type(Property::SymbolAvoidEdges) },
+ { "symbol-placement", mbgl::underlying_type(Property::SymbolPlacement) },
+ { "symbol-sort-key", mbgl::underlying_type(Property::SymbolSortKey) },
+ { "symbol-spacing", mbgl::underlying_type(Property::SymbolSpacing) },
+ { "symbol-z-order", mbgl::underlying_type(Property::SymbolZOrder) },
+ { "text-allow-overlap", mbgl::underlying_type(Property::TextAllowOverlap) },
+ { "text-anchor", mbgl::underlying_type(Property::TextAnchor) },
+ { "text-field", mbgl::underlying_type(Property::TextField) },
+ { "text-font", mbgl::underlying_type(Property::TextFont) },
+ { "text-ignore-placement", mbgl::underlying_type(Property::TextIgnorePlacement) },
+ { "text-justify", mbgl::underlying_type(Property::TextJustify) },
+ { "text-keep-upright", mbgl::underlying_type(Property::TextKeepUpright) },
+ { "text-letter-spacing", mbgl::underlying_type(Property::TextLetterSpacing) },
+ { "text-line-height", mbgl::underlying_type(Property::TextLineHeight) },
+ { "text-max-angle", mbgl::underlying_type(Property::TextMaxAngle) },
+ { "text-max-width", mbgl::underlying_type(Property::TextMaxWidth) },
+ { "text-offset", mbgl::underlying_type(Property::TextOffset) },
+ { "text-optional", mbgl::underlying_type(Property::TextOptional) },
+ { "text-padding", mbgl::underlying_type(Property::TextPadding) },
+ { "text-pitch-alignment", mbgl::underlying_type(Property::TextPitchAlignment) },
+ { "text-radial-offset", mbgl::underlying_type(Property::TextRadialOffset) },
+ { "text-rotate", mbgl::underlying_type(Property::TextRotate) },
+ { "text-rotation-alignment", mbgl::underlying_type(Property::TextRotationAlignment) },
+ { "text-size", mbgl::underlying_type(Property::TextSize) },
+ { "text-transform", mbgl::underlying_type(Property::TextTransform) },
+ { "text-variable-anchor", mbgl::underlying_type(Property::TextVariableAnchor) },
+ { "text-writing-mode", mbgl::underlying_type(Property::TextWritingMode) }
});
const auto it = properties.find(name.c_str());
@@ -1763,6 +1782,18 @@ optional<Error> SymbolLayer::setLayoutProperty(const std::string& name, const Co
}
+ if (property == Property::TextWritingMode) {
+ Error error;
+ optional<PropertyValue<std::vector<TextWritingModeType>>> typedValue = convert<PropertyValue<std::vector<TextWritingModeType>>>(value, error, false, false);
+ if (!typedValue) {
+ return error;
+ }
+
+ setTextWritingMode(*typedValue);
+ return nullopt;
+
+ }
+
return Error { "layer doesn't support this property" };
}
diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp
index 9b63e0e8d6..ca9505a5f3 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.hpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.hpp
@@ -74,7 +74,7 @@ struct FormatSectionOverrides<TypeList<PaintProperty...>> {
},
[&checkLiteral] (const PropertyExpression<TextField::Type>& property) {
bool expressionHasOverrides = false;
- const auto checkExpression = [&](const expression::Expression& e) {
+ const std::function<void(const expression::Expression&)> checkExpression = [&](const expression::Expression& e) {
if (expressionHasOverrides) {
return;
}
@@ -87,9 +87,7 @@ struct FormatSectionOverrides<TypeList<PaintProperty...>> {
expressionHasOverrides = true;
}
return;
- }
-
- if (e.getKind() == expression::Kind::FormatExpression) {
+ } else if (e.getKind() == expression::Kind::FormatExpression) {
const auto* formatExpr = static_cast<const expression::FormatExpression*>(&e);
for (const auto& section : formatExpr->getSections()) {
if (Property::hasOverride(section)) {
@@ -97,17 +95,12 @@ struct FormatSectionOverrides<TypeList<PaintProperty...>> {
break;
}
}
+ } else {
+ e.eachChild(checkExpression);
}
};
- // Check root property expression and return early.
checkExpression(property.getExpression());
- if (expressionHasOverrides) {
- return true;
- }
-
- // Traverse thru children and check whether any of them have overrides.
- property.getExpression().eachChild(checkExpression);
return expressionHasOverrides;
},
[] (const auto&) {
diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp
index 0c2bcd2661..59d65b3c86 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -214,6 +214,11 @@ struct TextVariableAnchor : LayoutProperty<std::vector<TextVariableAnchorType>>
static std::vector<TextVariableAnchorType> defaultValue() { return { }; }
};
+struct TextWritingMode : LayoutProperty<std::vector<TextWritingModeType>> {
+ static constexpr const char *name() { return "text-writing-mode"; }
+ static std::vector<TextWritingModeType> defaultValue() { return { }; }
+};
+
struct IconColor : DataDrivenPaintProperty<Color, attributes::fill_color, uniforms::fill_color> {
static Color defaultValue() { return Color::black(); }
};
@@ -313,7 +318,8 @@ class SymbolLayoutProperties : public Properties<
TextRotationAlignment,
TextSize,
TextTransform,
- TextVariableAnchor
+ TextVariableAnchor,
+ TextWritingMode
> {};
class SymbolPaintProperties : public Properties<
diff --git a/src/mbgl/style/light.cpp b/src/mbgl/style/light.cpp
index a88cc02bd7..b01f4220f2 100644
--- a/src/mbgl/style/light.cpp
+++ b/src/mbgl/style/light.cpp
@@ -3,6 +3,14 @@
#include <mbgl/style/light.hpp>
#include <mbgl/style/light_impl.hpp>
#include <mbgl/style/light_observer.hpp>
+#include <mbgl/style/conversion/light.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
+
+#include <mapbox/eternal.hpp>
namespace mbgl {
namespace style {
@@ -24,6 +32,118 @@ Mutable<Light::Impl> Light::mutableImpl() const {
return makeMutable<Impl>(*impl);
}
+using namespace conversion;
+
+optional<Error> Light::setProperty(const std::string& name, const Convertible& value) {
+ enum class Property {
+ Anchor,
+ Color,
+ Intensity,
+ Position,
+ AnchorTransition,
+ ColorTransition,
+ IntensityTransition,
+ PositionTransition,
+ };
+
+ MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
+ { "anchor", mbgl::underlying_type(Property::Anchor) },
+ { "color", mbgl::underlying_type(Property::Color) },
+ { "intensity", mbgl::underlying_type(Property::Intensity) },
+ { "position", mbgl::underlying_type(Property::Position) },
+ { "anchor-transition", mbgl::underlying_type(Property::AnchorTransition) },
+ { "color-transition", mbgl::underlying_type(Property::ColorTransition) },
+ { "intensity-transition", mbgl::underlying_type(Property::IntensityTransition) },
+ { "position-transition", mbgl::underlying_type(Property::PositionTransition) }
+ });
+
+ const auto it = properties.find(name.c_str());
+ if (it == properties.end()) {
+ return Error { "light doesn't support this property" };
+ }
+
+ auto property = static_cast<Property>(it->second);
+
+
+ if (property == Property::Anchor) {
+ Error error;
+ optional<PropertyValue<LightAnchorType>> typedValue = convert<PropertyValue<LightAnchorType>>(value, error, false, false);
+ if (!typedValue) {
+ return error;
+ }
+
+ setAnchor(*typedValue);
+ return nullopt;
+
+ }
+
+ if (property == Property::Color) {
+ Error error;
+ optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, false, false);
+ if (!typedValue) {
+ return error;
+ }
+
+ setColor(*typedValue);
+ return nullopt;
+
+ }
+
+ if (property == Property::Intensity) {
+ Error error;
+ optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false);
+ if (!typedValue) {
+ return error;
+ }
+
+ setIntensity(*typedValue);
+ return nullopt;
+
+ }
+
+ if (property == Property::Position) {
+ Error error;
+ optional<PropertyValue<Position>> typedValue = convert<PropertyValue<Position>>(value, error, false, false);
+ if (!typedValue) {
+ return error;
+ }
+
+ setPosition(*typedValue);
+ return nullopt;
+
+ }
+
+
+ Error error;
+ optional<TransitionOptions> transition = convert<TransitionOptions>(value, error);
+ if (!transition) {
+ return error;
+ }
+
+ if (property == Property::AnchorTransition) {
+ setAnchorTransition(*transition);
+ return nullopt;
+ }
+
+ if (property == Property::ColorTransition) {
+ setColorTransition(*transition);
+ return nullopt;
+ }
+
+ if (property == Property::IntensityTransition) {
+ setIntensityTransition(*transition);
+ return nullopt;
+ }
+
+ if (property == Property::PositionTransition) {
+ setPositionTransition(*transition);
+ return nullopt;
+ }
+
+
+ return Error { "light doesn't support this property" };
+}
+
LightAnchorType Light::getDefaultAnchor() {
return LightAnchor::defaultValue();
}
diff --git a/src/mbgl/style/light.cpp.ejs b/src/mbgl/style/light.cpp.ejs
index 45241c60fd..579889ab0d 100644
--- a/src/mbgl/style/light.cpp.ejs
+++ b/src/mbgl/style/light.cpp.ejs
@@ -6,6 +6,14 @@
#include <mbgl/style/light.hpp>
#include <mbgl/style/light_impl.hpp>
#include <mbgl/style/light_observer.hpp>
+#include <mbgl/style/conversion/light.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion_impl.hpp>
+#include <mbgl/util/traits.hpp>
+
+#include <mapbox/eternal.hpp>
namespace mbgl {
namespace style {
@@ -27,6 +35,76 @@ Mutable<Light::Impl> Light::mutableImpl() const {
return makeMutable<Impl>(*impl);
}
+using namespace conversion;
+
+optional<Error> Light::setProperty(const std::string& name, const Convertible& value) {
+ enum class Property {
+<% for (const property of properties) { -%>
+ <%- camelize(property.name) %>,
+<% } -%>
+<% for (const property of properties) { -%>
+ <%- camelize(property.name) %>Transition,
+<% } -%>
+ };
+
+ MAPBOX_ETERNAL_CONSTEXPR const auto properties = mapbox::eternal::hash_map<mapbox::eternal::string, uint8_t>({
+ <%- properties.map(p => `{ "${p.name}", mbgl::underlying_type(Property::${camelize(p.name)}) }`).join(',\n ') %>,
+ <%- properties.map(p => `{ "${p.name}-transition", mbgl::underlying_type(Property::${camelize(p.name)}Transition) }`).join(',\n ') %>
+ });
+
+ const auto it = properties.find(name.c_str());
+ if (it == properties.end()) {
+ return Error { "light doesn't support this property" };
+ }
+
+ auto property = static_cast<Property>(it->second);
+
+ <%
+ const conversions = {};
+ for (const property of properties) {
+ const dataDriven = property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven';
+ const convertTokens = property.name === 'icon-image' || property.name === 'text-field';
+ const conversion = `optional<${propertyValueType(property)}> typedValue = convert<${propertyValueType(property)}>(value, error, ${dataDriven}, ${convertTokens})`;
+ conversions[conversion] = conversions[conversion] || [];
+ conversions[conversion].push(property);
+ }
+ -%>
+ <% for (const key in conversions) {
+ const properties = conversions[key];
+ %>
+ if (<%- properties.map(p => `property == Property::${camelize(p.name)}`).join(' || ') %>) {
+ Error error;
+ <%- key %>;
+ if (!typedValue) {
+ return error;
+ }
+ <% if (properties.length == 1) { %>
+ set<%- camelize(properties[0].name) %>(*typedValue);
+ return nullopt;
+ <% } else for (const property of properties) { %>
+ if (property == Property::<%- camelize(property.name) %>) {
+ set<%- camelize(property.name) %>(*typedValue);
+ return nullopt;
+ }
+ <% } %>
+ }
+ <% } %>
+
+ Error error;
+ optional<TransitionOptions> transition = convert<TransitionOptions>(value, error);
+ if (!transition) {
+ return error;
+ }
+ <% for (const property of properties) { %>
+ if (property == Property::<%- camelize(property.name) %>Transition) {
+ set<%- camelize(property.name) %>Transition(*transition);
+ return nullopt;
+ }
+ <% } %>
+
+ return Error { "light doesn't support this property" };
+}
+
<% for (const property of properties) { -%>
<%- evaluatedType(property) %> Light::getDefault<%- camelize(property.name) %>() {
return Light<%- camelize(property.name) %>::defaultValue();
diff --git a/src/mbgl/style/sources/custom_geometry_source.cpp b/src/mbgl/style/sources/custom_geometry_source.cpp
index 6e9d8d65fb..73675c056f 100644
--- a/src/mbgl/style/sources/custom_geometry_source.cpp
+++ b/src/mbgl/style/sources/custom_geometry_source.cpp
@@ -1,6 +1,7 @@
#include <mbgl/style/sources/custom_geometry_source.hpp>
#include <mbgl/style/custom_tile_loader.hpp>
#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
#include <mbgl/actor/actor.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/tile/tile_id.hpp>
@@ -25,6 +26,7 @@ const CustomGeometrySource::Impl& CustomGeometrySource::impl() const {
void CustomGeometrySource::loadDescription(FileSource&) {
baseImpl = makeMutable<CustomGeometrySource::Impl>(impl(), loader->self());
loaded = true;
+ observer->onSourceLoaded(*this);
}
void CustomGeometrySource::setTileData(const CanonicalTileID& tileID,
diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp
index 4e3478322d..72a51e212f 100644
--- a/src/mbgl/style/sources/geojson_source.cpp
+++ b/src/mbgl/style/sources/geojson_source.cpp
@@ -9,7 +9,7 @@
namespace mbgl {
namespace style {
-GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions& options)
+GeoJSONSource::GeoJSONSource(const std::string& id, optional<GeoJSONOptions> options)
: Source(makeMutable<Impl>(std::move(id), options)) {
}
diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp
index 24ac1d7976..c3cb942709 100644
--- a/src/mbgl/style/sources/geojson_source_impl.cpp
+++ b/src/mbgl/style/sources/geojson_source_impl.cpp
@@ -1,6 +1,7 @@
#include <mbgl/style/sources/geojson_source_impl.hpp>
-#include <mbgl/util/constants.hpp>
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/feature.hpp>
#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
@@ -13,9 +14,9 @@ namespace style {
class GeoJSONVTData : public GeoJSONData {
public:
- GeoJSONVTData(const GeoJSON& geoJSON,
- const mapbox::geojsonvt::Options& options)
- : impl(geoJSON, options) {}
+ GeoJSONVTData(const GeoJSON& geoJSON, const mapbox::geojsonvt::Options& options)
+ : impl(geoJSON, options) {
+ }
mapbox::feature::feature_collection<int16_t> getTile(const CanonicalTileID& tileID) final {
return impl.getTile(tileID.z, tileID.x, tileID.y).features;
@@ -25,9 +26,8 @@ public:
return {};
}
- mapbox::feature::feature_collection<double> getLeaves(const std::uint32_t,
- const std::uint32_t,
- const std::uint32_t) final {
+ mapbox::feature::feature_collection<double>
+ getLeaves(const std::uint32_t, const std::uint32_t, const std::uint32_t) final {
return {};
}
@@ -43,7 +43,8 @@ class SuperclusterData : public GeoJSONData {
public:
SuperclusterData(const mapbox::feature::feature_collection<double>& features,
const mapbox::supercluster::Options& options)
- : impl(features, options) {}
+ : impl(features, options) {
+ }
mapbox::feature::feature_collection<int16_t> getTile(const CanonicalTileID& tileID) final {
return impl.getTile(tileID.z, tileID.x, tileID.y);
@@ -54,8 +55,8 @@ public:
}
mapbox::feature::feature_collection<double> getLeaves(const std::uint32_t cluster_id,
- const std::uint32_t limit,
- const std::uint32_t offset) final {
+ const std::uint32_t limit,
+ const std::uint32_t offset) final {
return impl.getLeaves(cluster_id, limit, offset);
}
@@ -67,23 +68,53 @@ private:
mapbox::supercluster::Supercluster impl;
};
-GeoJSONSource::Impl::Impl(std::string id_, GeoJSONOptions options_)
- : Source::Impl(SourceType::GeoJSON, std::move(id_)),
- options(std::move(options_)) {
+template <class T>
+T evaluateFeature(const mapbox::feature::feature<double>& f,
+ const std::shared_ptr<expression::Expression>& expression,
+ optional<T> accumulated = nullopt) {
+ const expression::EvaluationResult result = expression->evaluate(accumulated, f);
+ if (result) {
+ optional<T> typed = expression::fromExpressionValue<T>(*result);
+ if (typed) {
+ return std::move(*typed);
+ }
+ }
+ return T();
+}
+
+GeoJSONSource::Impl::Impl(std::string id_, optional<GeoJSONOptions> options_)
+ : Source::Impl(SourceType::GeoJSON, std::move(id_)) {
+ options = options_ ? std::move(*options_) : GeoJSONOptions{};
}
GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON)
- : Source::Impl(other),
- options(other.options) {
+ : Source::Impl(other), options(other.options) {
constexpr double scale = util::EXTENT / util::tileSize;
-
- if (options.cluster
- && geoJSON.is<mapbox::feature::feature_collection<double>>()
- && !geoJSON.get<mapbox::feature::feature_collection<double>>().empty()) {
+ if (options.cluster && geoJSON.is<mapbox::feature::feature_collection<double>>() &&
+ !geoJSON.get<mapbox::feature::feature_collection<double>>().empty()) {
mapbox::supercluster::Options clusterOptions;
clusterOptions.maxZoom = options.clusterMaxZoom;
clusterOptions.extent = util::EXTENT;
clusterOptions.radius = ::round(scale * options.clusterRadius);
+ Feature feature;
+ clusterOptions.map = [&](const PropertyMap& properties) -> PropertyMap {
+ PropertyMap ret{};
+ for (const auto& p : options.clusterProperties) {
+ feature.properties = properties;
+ ret[p.first] = evaluateFeature<Value>(feature, p.second.first);
+ }
+ return ret;
+ };
+ clusterOptions.reduce = [&](PropertyMap& toReturn, const PropertyMap& toFill) {
+ for (const auto& p : options.clusterProperties) {
+ if (toFill.count(p.first) == 0) {
+ continue;
+ }
+ feature.properties = toFill;
+ optional<Value> accumulated(toReturn[p.first]);
+ toReturn[p.first] = evaluateFeature<Value>(feature, p.second.second, accumulated);
+ }
+ };
data = std::make_shared<SuperclusterData>(
geoJSON.get<mapbox::feature::feature_collection<double>>(), clusterOptions);
} else {
diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp
index b88ab35ee0..26b9d95a39 100644
--- a/src/mbgl/style/sources/geojson_source_impl.hpp
+++ b/src/mbgl/style/sources/geojson_source_impl.hpp
@@ -26,7 +26,7 @@ public:
class GeoJSONSource::Impl : public Source::Impl {
public:
- Impl(std::string id, GeoJSONOptions);
+ Impl(std::string id, optional<GeoJSONOptions>);
Impl(const GeoJSONSource::Impl&, const GeoJSON&);
~Impl() final;
diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp
index b4fbe22ae1..115887d004 100644
--- a/src/mbgl/style/sources/raster_source.cpp
+++ b/src/mbgl/style/sources/raster_source.cpp
@@ -41,6 +41,7 @@ void RasterSource::loadDescription(FileSource& fileSource) {
if (urlOrTileset.is<Tileset>()) {
baseImpl = makeMutable<Impl>(impl(), urlOrTileset.get<Tileset>());
loaded = true;
+ observer->onSourceLoaded(*this);
return;
}
diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp
index 8fa694e4d0..a69ff632d8 100644
--- a/src/mbgl/style/sources/vector_source.cpp
+++ b/src/mbgl/style/sources/vector_source.cpp
@@ -11,10 +11,12 @@
namespace mbgl {
namespace style {
-VectorSource::VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset_)
+VectorSource::VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset_, optional<float> maxZoom_,
+ optional<float> minZoom_)
: Source(makeMutable<Impl>(std::move(id))),
- urlOrTileset(std::move(urlOrTileset_)) {
-}
+ urlOrTileset(std::move(urlOrTileset_)),
+ maxZoom(std::move(maxZoom_)),
+ minZoom(std::move(minZoom_)) {}
VectorSource::~VectorSource() = default;
@@ -38,6 +40,7 @@ void VectorSource::loadDescription(FileSource& fileSource) {
if (urlOrTileset.is<Tileset>()) {
baseImpl = makeMutable<Impl>(impl(), urlOrTileset.get<Tileset>());
loaded = true;
+ observer->onSourceLoaded(*this);
return;
}
@@ -60,7 +63,12 @@ void VectorSource::loadDescription(FileSource& fileSource) {
observer->onSourceError(*this, std::make_exception_ptr(util::StyleParseException(error.message)));
return;
}
-
+ if (maxZoom) {
+ tileset->zoomRange.max = *maxZoom;
+ }
+ if (minZoom) {
+ tileset->zoomRange.min = *minZoom;
+ }
util::mapbox::canonicalizeTileset(*tileset, url, getType(), util::tileSize);
bool changed = impl().tileset != *tileset;
diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp
index 10fee73cdd..d3298c5cac 100644
--- a/src/mbgl/style/style_impl.cpp
+++ b/src/mbgl/style/style_impl.cpp
@@ -140,9 +140,8 @@ void Style::Impl::addSource(std::unique_ptr<Source> source) {
}
source->setObserver(this);
- source->loadDescription(fileSource);
-
- sources.add(std::move(source));
+ auto item = sources.add(std::move(source));
+ item->loadDescription(fileSource);
}
std::unique_ptr<Source> Style::Impl::removeSource(const std::string& id) {
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
index f388a9385f..c4a7b76b66 100644
--- a/src/mbgl/style/types.cpp
+++ b/src/mbgl/style/types.cpp
@@ -96,6 +96,11 @@ MBGL_DEFINE_ENUM(TextTransformType, {
{ TextTransformType::Lowercase, "lowercase" },
});
+MBGL_DEFINE_ENUM(TextWritingModeType, {
+ { TextWritingModeType::Horizontal, "horizontal" },
+ { TextWritingModeType::Vertical, "vertical" }
+});
+
MBGL_DEFINE_ENUM(AlignmentType, {
{ AlignmentType::Map, "map" },
{ AlignmentType::Viewport, "viewport" },
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index c708f834da..f9f6b3a4a5 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -54,9 +54,9 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const float yMin = std::min({tl.y, tr.y, bl.y, br.y});
const float yMax = std::max({tl.y, tr.y, bl.y, br.y});
- boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, xMin, yMin, xMax, yMax);
+ boxes.emplace_back(anchor.point, xMin, yMin, xMax, yMax);
} else {
- boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, x1, y1, x2, y2);
+ boxes.emplace_back(anchor.point, x1, y1, x2, y2);
}
}
}
@@ -155,7 +155,7 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
0 :
(boxDistanceToAnchor - firstBoxOffset) * 0.8;
- boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, paddedAnchorDistance, boxSize / 2);
+ boxes.emplace_back(boxAnchor, -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, paddedAnchorDistance);
}
}
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index 5fbd8a5e4b..4afecd8e73 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -9,16 +9,53 @@
namespace mbgl {
+class ProjectedCollisionBox {
+public:
+ enum class Type : char {
+ Unknown,
+ Box,
+ Circle
+ };
+
+ ProjectedCollisionBox() = default;
+ ProjectedCollisionBox(float x1, float y1, float x2, float y2) : geometry(x1, y1, x2, y2), type(Type::Box) {}
+ ProjectedCollisionBox(float x, float y, float r) : geometry(x, y, r), type(Type::Circle) {}
+
+ const mapbox::geometry::box<float>& box() const {
+ assert(isBox());
+ return geometry.box;
+ }
+
+ const geometry::circle<float>& circle() const {
+ assert(isCircle());
+ return geometry.circle;
+ }
+
+ bool isCircle() const { return type == Type::Circle; }
+ bool isBox() const { return type == Type::Box; }
+
+private:
+ union Geometry {
+ Geometry(float x1, float y1, float x2, float y2) : box({x1, y1}, {x2, y2}) {}
+ Geometry(float x, float y, float r) : circle({x, y}, r) {}
+ Geometry() {}
+ mapbox::geometry::box<float> box;
+ geometry::circle<float> circle;
+ } geometry;
+ Type type = Type::Unknown;
+};
+
class CollisionBox {
public:
- CollisionBox(Point<float> _anchor, Point<float> _offset, float _x1, float _y1, float _x2, float _y2, float _signedDistanceFromAnchor = 0, float _radius = 0) :
- anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), used(true), signedDistanceFromAnchor(_signedDistanceFromAnchor), radius(_radius) {}
+ CollisionBox(Point<float> _anchor, float _x1, float _y1, float _x2, float _y2, float _signedDistanceFromAnchor = 0) :
+ anchor(std::move(_anchor)), x1(_x1), y1(_y1), x2(_x2), y2(_y2), signedDistanceFromAnchor(_signedDistanceFromAnchor) {}
// 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;
+
+ // the offset of the box from the label's anchor point.
+ // TODO: might be needed for #13526
+ // Point<float> offset;
// distances to the edges from the anchor
float x1;
@@ -26,19 +63,7 @@ public:
float x2;
float y2;
- // Projected box geometry: generated/updated at placement time
- float px1;
- float py1;
- float px2;
- float py2;
-
- // Projected circle geometry: generated/updated at placement time
- float px;
- float py;
- bool used;
-
float signedDistanceFromAnchor;
- float radius;
};
class CollisionFeature {
diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp
index 74923312d5..c83b117b9a 100644
--- a/src/mbgl/text/collision_index.cpp
+++ b/src/mbgl/text/collision_index.cpp
@@ -55,12 +55,12 @@ float CollisionIndex::approximateTileDistance(const TileDistance& tileDistance,
(incidenceStretch - 1) * lastSegmentTile * std::abs(std::sin(lastSegmentAngle));
}
-bool CollisionIndex::isOffscreen(const CollisionBox& box) const {
- return box.px2 < viewportPadding || box.px1 >= screenRightBoundary || box.py2 < viewportPadding || box.py1 >= screenBottomBoundary;
+bool CollisionIndex::isOffscreen(float x1, float y1, float x2, float y2) const {
+ return x2 < viewportPadding || x1 >= screenRightBoundary || y2 < viewportPadding || y1 >= screenBottomBoundary;
}
-bool CollisionIndex::isInsideGrid(const CollisionBox& box) const {
- return box.px2 >= 0 && box.px1 < gridRightBoundary && box.py2 >= 0 && box.py1 < gridBottomBoundary;
+bool CollisionIndex::isInsideGrid(float x1, float y1, float x2, float y2) const {
+ return x2 >= 0 && x1 < gridRightBoundary && y2 >= 0 && y1 < gridBottomBoundary;
}
CollisionTileBoundaries CollisionIndex::projectTileBoundaries(const mat4& posMatrix) const {
@@ -71,62 +71,66 @@ CollisionTileBoundaries CollisionIndex::projectTileBoundaries(const mat4& posMat
}
-bool CollisionIndex::isInsideTile(const CollisionBox& box, const CollisionTileBoundaries& tileBoundaries) const {
+bool CollisionIndex::isInsideTile(float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const {
// This check is only well defined when the tile boundaries are axis-aligned
// We are relying on it only being used in MapMode::Tile, where that is always the case
- return box.px1 >= tileBoundaries[0] && box.py1 >= tileBoundaries[1] && box.px2 < tileBoundaries[2] && box.py2 < tileBoundaries[3];
+ return x1 >= tileBoundaries[0] && y1 >= tileBoundaries[1] && x2 < tileBoundaries[2] && y2 < tileBoundaries[3];
}
-std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
+std::pair<bool,bool> CollisionIndex::placeFeature(const CollisionFeature& feature,
Point<float> shift,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
- PlacedSymbol& symbol,
+ const PlacedSymbol& symbol,
const float scale,
const float fontSize,
const bool allowOverlap,
const bool pitchWithMap,
const bool collisionDebug,
const optional<CollisionTileBoundaries>& avoidEdges,
- const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate) {
+ const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
+ std::vector<ProjectedCollisionBox>& projectedBoxes) {
+ assert(projectedBoxes.empty());
if (!feature.alongLine) {
- CollisionBox& box = feature.boxes.front();
+ const CollisionBox& box = feature.boxes.front();
const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor);
const float tileToViewport = textPixelRatio * projectedPoint.second;
- box.px1 = (box.x1 + shift.x) * tileToViewport + projectedPoint.first.x;
- box.py1 = (box.y1 + shift.y) * tileToViewport + projectedPoint.first.y;
- box.px2 = (box.x2 + shift.x) * tileToViewport + projectedPoint.first.x;
- box.py2 = (box.y2 + shift.y) * tileToViewport + projectedPoint.first.y;
-
-
- if ((avoidEdges && !isInsideTile(box, *avoidEdges)) ||
- !isInsideGrid(box) ||
- (!allowOverlap && collisionGrid.hitTest({{ box.px1, box.py1 }, { box.px2, box.py2 }}, collisionGroupPredicate))) {
+ float px1 = (box.x1 + shift.x) * tileToViewport + projectedPoint.first.x;
+ float py1 = (box.y1 + shift.y) * tileToViewport + projectedPoint.first.y;
+ float px2 = (box.x2 + shift.x) * tileToViewport + projectedPoint.first.x;
+ float py2 = (box.y2 + shift.y) * tileToViewport + projectedPoint.first.y;
+ projectedBoxes.emplace_back(px1, py1, px2, py2);
+
+ if ((avoidEdges && !isInsideTile(px1, py1, px2, py2, *avoidEdges)) ||
+ !isInsideGrid(px1, py1, px2, py2) ||
+ (!allowOverlap && collisionGrid.hitTest(projectedBoxes.back().box(), collisionGroupPredicate))) {
return { false, false };
}
- return {true, isOffscreen(box)};
+ return {true, isOffscreen(px1, py1, px2, py2)};
} else {
- return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug, avoidEdges, collisionGroupPredicate);
+ return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug, avoidEdges, collisionGroupPredicate, projectedBoxes);
}
}
-std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
+std::pair<bool,bool> CollisionIndex::placeLineFeature(const CollisionFeature& feature,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
- PlacedSymbol& symbol,
+ const PlacedSymbol& symbol,
const float scale,
const float fontSize,
const bool allowOverlap,
const bool pitchWithMap,
const bool collisionDebug,
const optional<CollisionTileBoundaries>& avoidEdges,
- const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate) {
-
+ const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
+ std::vector<ProjectedCollisionBox>& projectedBoxes) {
+ assert(feature.alongLine);
+ assert(projectedBoxes.empty());
const auto tileUnitAnchorPoint = symbol.anchorPoint;
const auto projectedAnchor = projectAnchor(posMatrix, tileUnitAnchorPoint);
@@ -163,9 +167,10 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
lastTileDistance = approximateTileDistance(*(firstAndLastGlyph->second.tileDistance), firstAndLastGlyph->second.angle, pixelsToTileUnits, projectedAnchor.second, pitchWithMap);
}
- bool atLeastOneCirclePlaced = false;
+ bool previousCirclePlaced = false;
+ projectedBoxes.resize(feature.boxes.size());
for (size_t i = 0; i < feature.boxes.size(); i++) {
- CollisionBox& circle = feature.boxes[i];
+ const CollisionBox& circle = feature.boxes[i];
const float boxSignedDistanceFromAnchor = circle.signedDistanceFromAnchor;
if (!firstAndLastGlyph ||
(boxSignedDistanceFromAnchor < -firstTileDistance) ||
@@ -173,7 +178,7 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
// The label either doesn't fit on its line or we
// don't need to use this circle because the label
// doesn't extend this far. Either way, mark the circle unused.
- circle.used = false;
+ previousCirclePlaced = false;
continue;
}
@@ -181,10 +186,12 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
const float tileUnitRadius = (circle.x2 - circle.x1) / 2;
const float radius = tileUnitRadius * tileToViewport;
- if (atLeastOneCirclePlaced) {
- const CollisionBox& previousCircle = feature.boxes[i - 1];
- const float dx = projectedPoint.x - previousCircle.px;
- const float dy = projectedPoint.y - previousCircle.py;
+ if (previousCirclePlaced) {
+ const ProjectedCollisionBox& previousCircle = projectedBoxes[i - 1];
+ assert(previousCircle.isCircle());
+ const auto& previousCenter = previousCircle.circle().center;
+ const float dx = projectedPoint.x - previousCenter.x;
+ const float dy = projectedPoint.y - previousCenter.y;
// The circle edges touch when the distance between their centers is 2x the radius
// When the distance is 1x the radius, they're doubled up, and we could remove
// every other circle while keeping them all in touch.
@@ -202,30 +209,26 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
// Hide significantly overlapping circles, unless this is the last one we can
// use, in which case we want to keep it in place even if it's tightly packed
// with the one before it.
- circle.used = false;
+ previousCirclePlaced = false;
continue;
}
}
}
}
- atLeastOneCirclePlaced = true;
- circle.px1 = projectedPoint.x - radius;
- circle.px2 = projectedPoint.x + radius;
- circle.py1 = projectedPoint.y - radius;
- circle.py2 = projectedPoint.y + radius;
-
- circle.used = true;
-
- circle.px = projectedPoint.x;
- circle.py = projectedPoint.y;
- circle.radius = radius;
+ previousCirclePlaced = true;
+ float px1 = projectedPoint.x - radius;
+ float px2 = projectedPoint.x + radius;
+ float py1 = projectedPoint.y - radius;
+ float py2 = projectedPoint.y + radius;
+
+ projectedBoxes[i] = ProjectedCollisionBox{projectedPoint.x, projectedPoint.y, radius};
- entirelyOffscreen &= isOffscreen(circle);
- inGrid |= isInsideGrid(circle);
+ entirelyOffscreen &= isOffscreen(px1, py1, px2, py2);
+ inGrid |= isInsideGrid(px1, py1, px2, py2);
- if ((avoidEdges && !isInsideTile(circle, *avoidEdges)) ||
- (!allowOverlap && collisionGrid.hitTest({{circle.px, circle.py}, circle.radius}, collisionGroupPredicate))) {
+ if ((avoidEdges && !isInsideTile(px1, py1, px2, py2, *avoidEdges)) ||
+ (!allowOverlap && collisionGrid.hitTest(projectedBoxes[i].circle(), collisionGroupPredicate))) {
if (!collisionDebug) {
return {false, false};
} else {
@@ -240,37 +243,38 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature,
}
-void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId, uint16_t collisionGroupId) {
+void CollisionIndex::insertFeature(const CollisionFeature& feature, const std::vector<ProjectedCollisionBox>& projectedBoxes, bool ignorePlacement, uint32_t bucketInstanceId, uint16_t collisionGroupId) {
if (feature.alongLine) {
- for (auto& circle : feature.boxes) {
- if (!circle.used) {
+ for (auto& circle : projectedBoxes) {
+ if (!circle.isCircle()) {
continue;
}
if (ignorePlacement) {
ignoredGrid.insert(
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
- {{ circle.px, circle.py }, circle.radius}
+ circle.circle()
);
} else {
collisionGrid.insert(
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
- {{ circle.px, circle.py }, circle.radius}
+ circle.circle()
);
}
}
} else {
- assert(feature.boxes.size() == 1);
- auto& box = feature.boxes[0];
+ assert(projectedBoxes.size() == 1);
+ auto& box = projectedBoxes[0];
+ assert(box.isBox());
if (ignorePlacement) {
ignoredGrid.insert(
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
- {{ box.px1, box.py1 }, { box.px2, box.py2 }}
+ box.box()
);
} else {
collisionGrid.insert(
IndexedSubfeature(feature.indexedFeature, bucketInstanceId, collisionGroupId),
- {{ box.px1, box.py1 }, { box.px2, box.py2 }}
+ box.box()
);
}
}
diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp
index ea65a426fd..4e8a2fdb62 100644
--- a/src/mbgl/text/collision_index.hpp
+++ b/src/mbgl/text/collision_index.hpp
@@ -22,21 +22,22 @@ public:
explicit CollisionIndex(const TransformState&);
- std::pair<bool,bool> placeFeature(CollisionFeature& feature,
+ std::pair<bool,bool> placeFeature(const CollisionFeature& feature,
Point<float> shift,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
- PlacedSymbol& symbol,
+ const PlacedSymbol& symbol,
const float scale,
const float fontSize,
const bool allowOverlap,
const bool pitchWithMap,
const bool collisionDebug,
const optional<CollisionTileBoundaries>& avoidEdges,
- const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate);
+ const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
+ std::vector<ProjectedCollisionBox>& /*out*/);
- void insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId, uint16_t collisionGroupId);
+ void insertFeature(const CollisionFeature& feature, const std::vector<ProjectedCollisionBox>&, bool ignorePlacement, uint32_t bucketInstanceId, uint16_t collisionGroupId);
std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> queryRenderedSymbols(const ScreenLineString&) const;
@@ -45,22 +46,23 @@ public:
const TransformState& getTransformState() const { return transformState; }
private:
- bool isOffscreen(const CollisionBox&) const;
- bool isInsideGrid(const CollisionBox&) const;
- bool isInsideTile(const CollisionBox&, const CollisionTileBoundaries& tileBoundaries) const;
+ bool isOffscreen(float x1, float y1, float x2, float y2) const;
+ bool isInsideGrid(float x1, float y1, float x2, float y2) const;
+ bool isInsideTile(float x1, float y1, float x2, float y2, const CollisionTileBoundaries& tileBoundaries) const;
- std::pair<bool,bool> placeLineFeature(CollisionFeature& feature,
+ std::pair<bool,bool> placeLineFeature(const CollisionFeature& feature,
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const float textPixelRatio,
- PlacedSymbol& symbol,
+ const PlacedSymbol& symbol,
const float scale,
const float fontSize,
const bool allowOverlap,
const bool pitchWithMap,
const bool collisionDebug,
const optional<CollisionTileBoundaries>& avoidEdges,
- const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate);
+ const optional<std::function<bool(const IndexedSubfeature&)>> collisionGroupPredicate,
+ std::vector<ProjectedCollisionBox>& /*out*/);
float approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap);
diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp
index 43ed85d957..55ab2cc3c5 100644
--- a/src/mbgl/text/cross_tile_symbol_index.cpp
+++ b/src/mbgl/text/cross_tile_symbol_index.cpp
@@ -163,10 +163,10 @@ bool CrossTileSymbolLayerIndex::removeStaleBuckets(const std::unordered_set<uint
CrossTileSymbolIndex::CrossTileSymbolIndex() = default;
-bool CrossTileSymbolIndex::addLayer(const RenderLayer& layer, float lng) {
+auto CrossTileSymbolIndex::addLayer(const RenderLayer& layer, float lng) -> AddLayerResult {
auto& layerIndex = layerIndexes[layer.getID()];
- bool symbolBucketsChanged = false;
+ AddLayerResult result = AddLayerResult::NoChanges;
std::unordered_set<uint32_t> currentBucketIDs;
layerIndex.handleWrapJump(lng);
@@ -174,16 +174,15 @@ bool CrossTileSymbolIndex::addLayer(const RenderLayer& layer, float lng) {
for (const auto& item : layer.getPlacementData()) {
const RenderTile& renderTile = item.tile;
Bucket& bucket = item.bucket;
- auto result = bucket.registerAtCrossTileIndex(layerIndex, renderTile.getOverscaledTileID(), maxCrossTileID);
- assert(result.first != 0u);
- symbolBucketsChanged = symbolBucketsChanged || result.second;
- currentBucketIDs.insert(result.first);
+ auto pair = bucket.registerAtCrossTileIndex(layerIndex, renderTile.getOverscaledTileID(), maxCrossTileID);
+ assert(pair.first != 0u);
+ if (pair.second) result |= AddLayerResult::BucketsAdded;
+ currentBucketIDs.insert(pair.first);
}
- if (layerIndex.removeStaleBuckets(currentBucketIDs)) {
- symbolBucketsChanged = true;
- }
- return symbolBucketsChanged;
+ if (layerIndex.removeStaleBuckets(currentBucketIDs)) result |= AddLayerResult::BucketsRemoved;
+
+ return result;
}
void CrossTileSymbolIndex::pruneUnusedLayers(const std::set<std::string>& usedLayers) {
diff --git a/src/mbgl/text/cross_tile_symbol_index.hpp b/src/mbgl/text/cross_tile_symbol_index.hpp
index d905aeb569..4e32698b3e 100644
--- a/src/mbgl/text/cross_tile_symbol_index.hpp
+++ b/src/mbgl/text/cross_tile_symbol_index.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/bitmask_operations.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/optional.hpp>
@@ -58,7 +59,13 @@ class CrossTileSymbolIndex {
public:
CrossTileSymbolIndex();
- bool addLayer(const RenderLayer& layer, float lng);
+ enum class AddLayerResult : uint8_t {
+ NoChanges = 0,
+ BucketsAdded = 1 << 0,
+ BucketsRemoved = 1 << 1
+ };
+
+ AddLayerResult addLayer(const RenderLayer& layer, float lng);
void pruneUnusedLayers(const std::set<std::string>&);
void reset();
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 5105528512..ba9c521f77 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/text/glyph_range.hpp>
+#include <mbgl/util/bitmask_operations.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/rect.hpp>
#include <mbgl/util/traits.hpp>
@@ -89,6 +90,8 @@ class Shaping {
WritingModeType writingMode;
std::size_t lineCount = 0u;
explicit operator bool() const { return !positionedGlyphs.empty(); }
+ // The y offset *should* be part of the font metadata.
+ static constexpr int32_t yOffset = -17;
};
enum class WritingModeType : uint8_t {
@@ -97,26 +100,6 @@ enum class WritingModeType : uint8_t {
Vertical = 1 << 1,
};
-MBGL_CONSTEXPR WritingModeType operator|(WritingModeType a, WritingModeType b) {
- return WritingModeType(mbgl::underlying_type(a) | mbgl::underlying_type(b));
-}
-
-MBGL_CONSTEXPR WritingModeType& operator|=(WritingModeType& a, WritingModeType b) {
- return (a = a | b);
-}
-
-MBGL_CONSTEXPR bool operator&(WritingModeType lhs, WritingModeType rhs) {
- return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
-}
-
-MBGL_CONSTEXPR WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) {
- return (lhs = WritingModeType(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs)));
-}
-
-MBGL_CONSTEXPR WritingModeType operator~(WritingModeType value) {
- return WritingModeType(~mbgl::underlying_type(value));
-}
-
using GlyphDependencies = std::map<FontStack, GlyphIDs>;
using GlyphRangeDependencies = std::map<FontStack, std::unordered_set<GlyphRange>>;
diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp
index daa142e38f..35ea1031d5 100644
--- a/src/mbgl/text/glyph_manager.cpp
+++ b/src/mbgl/text/glyph_manager.cpp
@@ -98,8 +98,11 @@ void GlyphManager::processResponse(const Response& res, const FontStack& fontSta
}
for (auto& glyph : glyphs) {
- entry.glyphs.erase(glyph.id);
- entry.glyphs.emplace(glyph.id, makeMutable<Glyph>(std::move(glyph)));
+ auto id = glyph.id;
+ if (!localGlyphRasterizer->canRasterizeGlyph(fontStack, id)) {
+ entry.glyphs.erase(id);
+ entry.glyphs.emplace(id, makeMutable<Glyph>(std::move(glyph)));
+ }
}
}
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
index 79bb984aa2..06777dea44 100644
--- a/src/mbgl/text/placement.cpp
+++ b/src/mbgl/text/placement.cpp
@@ -62,6 +62,7 @@ Placement::Placement(const TransformState& state_, MapMode mapMode_, style::Tran
: collisionIndex(state_)
, mapMode(mapMode_)
, transitionOptions(std::move(transitionOptions_))
+ , placementZoom(state_.getZoom())
, collisionGroups(crossSourceCollisions)
, prevPlacement(std::move(prevPlacement_))
{
@@ -85,20 +86,18 @@ void Placement::placeLayer(const RenderLayer& layer, const mat4& projMatrix, boo
}
namespace {
-Point<float> calculateVariableLayoutOffset(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale) {
+Point<float> calculateVariableLayoutOffset(style::SymbolAnchorType anchor, float width, float height, std::array<float, 2> offset, float textBoxScale) {
AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor);
float shiftX = -(alignment.horizontalAlign - 0.5f) * width;
float shiftY = -(alignment.verticalAlign - 0.5f) * height;
- Point<float> offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset);
- return Point<float>(
- shiftX + offset.x * textBoxScale,
- shiftY + offset.y * textBoxScale
- );
+ auto variableOffset = SymbolLayout::evaluateVariableOffset(anchor, offset);
+ return { shiftX + variableOffset[0] * textBoxScale,
+ shiftY + variableOffset[1] * textBoxScale };
}
} // namespace
void Placement::placeBucket(
- SymbolBucket& bucket,
+ const SymbolBucket& bucket,
const BucketPlacementParameters& params,
std::set<uint32_t>& seenCrossTileIDs) {
const auto& layout = *bucket.layout;
@@ -152,15 +151,18 @@ void Placement::placeBucket(
// This is the reverse of our normal policy of "fade in on pan", but should look like any other
// collision and hopefully not be too noticeable.
// See https://github.com/mapbox/mapbox-gl-native/issues/12683
- const bool alwaysShowText = textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || layout.get<style::IconOptional>());
+ const bool alwaysShowText = textAllowOverlap && (iconAllowOverlap || !(bucket.hasIconData() || bucket.hasSdfIconData()) || layout.get<style::IconOptional>());
const bool alwaysShowIcon = iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || layout.get<style::TextOptional>());
std::vector<style::TextVariableAnchorType> variableTextAnchors = layout.get<style::TextVariableAnchor>();
const bool rotateWithMap = layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map;
const bool pitchWithMap = layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map;
+ const bool hasIconTextFit = layout.get<style::IconTextFit>() != style::IconTextFitType::None;
const bool zOrderByViewportY = layout.get<style::SymbolZOrder>() == style::SymbolZOrderType::ViewportY;
+ std::vector<ProjectedCollisionBox> textBoxes;
+ std::vector<ProjectedCollisionBox> iconBoxes;
- auto placeSymbol = [&] (SymbolInstance& symbolInstance) {
+ auto placeSymbol = [&] (const SymbolInstance& symbolInstance) {
if (seenCrossTileIDs.count(symbolInstance.crossTileID) != 0u) return;
if (renderTile.holdForFade()) {
@@ -169,30 +171,83 @@ void Placement::placeBucket(
placements.emplace(symbolInstance.crossTileID, JointPlacement(false, false, false));
return;
}
+ textBoxes.clear();
+ iconBoxes.clear();
bool placeText = false;
bool placeIcon = false;
bool offscreen = true;
+ std::pair<bool, bool> placed{ false, false };
+ std::pair<bool, bool> placedVerticalText{ false, false };
+ std::pair<bool, bool> placedVerticalIcon{ false, false };
+ Point<float> shift{0.0f, 0.0f};
optional<size_t> horizontalTextIndex = symbolInstance.getDefaultHorizontalPlacedTextIndex();
if (horizontalTextIndex) {
- CollisionFeature& textCollisionFeature = symbolInstance.textCollisionFeature;
- PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*horizontalTextIndex);
+ const PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*horizontalTextIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
+ const CollisionFeature& textCollisionFeature = symbolInstance.textCollisionFeature;
+
+ const auto updatePreviousOrientationIfNotPlaced = [&](bool isPlaced) {
+ if (bucket.allowVerticalPlacement && !isPlaced && prevPlacement) {
+ auto prevOrientation = prevPlacement->placedOrientations.find(symbolInstance.crossTileID);
+ if (prevOrientation != prevPlacement->placedOrientations.end()) {
+ placedOrientations[symbolInstance.crossTileID] = prevOrientation->second;
+ }
+ }
+ };
+
+ const auto placeTextForPlacementModes = [&] (auto& placeHorizontalFn, auto& placeVerticalFn) {
+ if (bucket.allowVerticalPlacement && symbolInstance.writingModes & WritingModeType::Vertical) {
+ assert(!bucket.placementModes.empty());
+ for (auto& placementMode : bucket.placementModes) {
+ if (placementMode == style::TextWritingModeType::Vertical) {
+ placedVerticalText = placed = placeVerticalFn();
+ } else {
+ placed = placeHorizontalFn();
+ }
+
+ if (placed.first) {
+ break;
+ }
+ }
+ } else {
+ placed = placeHorizontalFn();
+ }
+ };
+
+ // Line or point label placement
if (variableTextAnchors.empty()) {
- auto placed = collisionIndex.placeFeature(textCollisionFeature, {},
- posMatrix, textLabelPlaneMatrix, pixelRatio,
- placedSymbol, scale, fontSize,
- layout.get<style::TextAllowOverlap>(),
- pitchWithMap,
- params.showCollisionBoxes, avoidEdges, collisionGroup.second);
+ const auto placeFeature = [&] (const CollisionFeature& collisionFeature, style::TextWritingModeType orientation) {
+ textBoxes.clear();
+ auto placedFeature = collisionIndex.placeFeature(collisionFeature, {},
+ posMatrix, textLabelPlaneMatrix, pixelRatio,
+ placedSymbol, scale, fontSize,
+ layout.get<style::TextAllowOverlap>(),
+ pitchWithMap,
+ params.showCollisionBoxes, avoidEdges, collisionGroup.second, textBoxes);
+ if (placedFeature.first) {
+ placedOrientations.emplace(symbolInstance.crossTileID, orientation);
+ }
+ return placedFeature;
+ };
+
+ const auto placeHorizontal = [&] {
+ return placeFeature(symbolInstance.textCollisionFeature, style::TextWritingModeType::Horizontal);
+ };
+
+ const auto placeVertical = [&] {
+ if (bucket.allowVerticalPlacement && symbolInstance.verticalTextCollisionFeature) {
+ return placeFeature(*symbolInstance.verticalTextCollisionFeature, style::TextWritingModeType::Vertical);
+ }
+ return std::pair<bool, bool>{false, false};
+ };
+
+ placeTextForPlacementModes(placeHorizontal, placeVertical);
+ updatePreviousOrientationIfNotPlaced(placed.first);
+
placeText = placed.first;
offscreen &= placed.second;
} else if (!textCollisionFeature.alongLine && !textCollisionFeature.boxes.empty()) {
- const CollisionBox& textBox = symbolInstance.textCollisionFeature.boxes[0];
- const float width = textBox.x2 - textBox.x1;
- const float height = textBox.y2 - textBox.y1;
- const float textBoxScale = symbolInstance.textBoxScale;
-
// If this symbol was in the last placement, shift the previously used
// anchor to the front of the anchor list, only if the previous anchor
// is still in the anchor list.
@@ -215,80 +270,125 @@ void Placement::placeBucket(
}
}
- for (auto anchor : variableTextAnchors) {
- Point<float> shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale);
- if (rotateWithMap) {
- float angle = pitchWithMap ? state.getBearing() : -state.getBearing();
- shift = util::rotate(shift, angle);
- }
+ const auto placeFeatureForVariableAnchors = [&] (const CollisionFeature& collisionFeature, style::TextWritingModeType orientation) {
+ const CollisionBox& textBox = collisionFeature.boxes[0];
+ const float width = textBox.x2 - textBox.x1;
+ const float height = textBox.y2 - textBox.y1;
+ const float textBoxScale = symbolInstance.textBoxScale;
+ std::pair<bool, bool> placedFeature = {false, false};
+ const size_t anchorsSize = variableTextAnchors.size();
+ const size_t placementAttempts = textAllowOverlap ? anchorsSize * 2 : anchorsSize;
+ for (size_t i = 0u; i < placementAttempts; ++i) {
+ auto anchor = variableTextAnchors[i % anchorsSize];
+ const bool allowOverlap = (i >= anchorsSize);
+ shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.variableTextOffset, textBoxScale);
+ if (rotateWithMap) {
+ float angle = pitchWithMap ? state.getBearing() : -state.getBearing();
+ shift = util::rotate(shift, angle);
+ }
+
+ textBoxes.clear();
+ placedFeature = collisionIndex.placeFeature(collisionFeature, shift,
+ posMatrix, mat4(), pixelRatio,
+ placedSymbol, scale, fontSize,
+ allowOverlap,
+ pitchWithMap,
+ params.showCollisionBoxes, avoidEdges, collisionGroup.second, textBoxes);
+ if (placedFeature.first) {
+ assert(symbolInstance.crossTileID != 0u);
+ optional<style::TextVariableAnchorType> prevAnchor;
+
+ // If this label was placed in the previous placement, record the anchor position
+ // to allow us to animate the transition
+ if (prevPlacement) {
+ auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
+ auto prevPlacements = prevPlacement->placements.find(symbolInstance.crossTileID);
+ if (prevOffset != prevPlacement->variableOffsets.end() &&
+ prevPlacements != prevPlacement->placements.end() &&
+ prevPlacements->second.text) {
+ // TODO: The prevAnchor seems to be unused, needs to be fixed.
+ prevAnchor = prevOffset->second.anchor;
+ }
+ }
- auto placed = collisionIndex.placeFeature(textCollisionFeature, shift,
- posMatrix, mat4(), pixelRatio,
- placedSymbol, scale, fontSize,
- layout.get<style::TextAllowOverlap>(),
- pitchWithMap,
- params.showCollisionBoxes, avoidEdges, collisionGroup.second);
-
- if (placed.first) {
- assert(symbolInstance.crossTileID != 0u);
- optional<style::TextVariableAnchorType> prevAnchor;
-
- // If this label was placed in the previous placement, record the anchor position
- // to allow us to animate the transition
- if (prevPlacement) {
- auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
- auto prevPlacements = prevPlacement->placements.find(symbolInstance.crossTileID);
- if (prevOffset != prevPlacement->variableOffsets.end() &&
- prevPlacements != prevPlacement->placements.end() &&
- prevPlacements->second.text) {
- prevAnchor = prevOffset->second.anchor;
+ variableOffsets.insert(std::make_pair(symbolInstance.crossTileID, VariableOffset{
+ symbolInstance.variableTextOffset,
+ width,
+ height,
+ anchor,
+ textBoxScale,
+ prevAnchor
+ }));
+
+ if (bucket.allowVerticalPlacement) {
+ placedOrientations.emplace(symbolInstance.crossTileID, orientation);
}
+ break;
}
+ }
+
+ return placedFeature;
+ };
- variableOffsets.insert(std::make_pair(symbolInstance.crossTileID, VariableOffset{
- symbolInstance.radialTextOffset,
- width,
- height,
- anchor,
- textBoxScale,
- prevAnchor
- }));
- markUsedJustification(bucket, anchor, symbolInstance);
-
- placeText = placed.first;
- offscreen &= placed.second;
- break;
+ const auto placeHorizontal = [&] {
+ return placeFeatureForVariableAnchors(symbolInstance.textCollisionFeature, style::TextWritingModeType::Horizontal);
+ };
+
+ const auto placeVertical = [&] {
+ if (bucket.allowVerticalPlacement && !placed.first && symbolInstance.verticalTextCollisionFeature) {
+ return placeFeatureForVariableAnchors(*symbolInstance.verticalTextCollisionFeature, style::TextWritingModeType::Vertical);
}
- }
+ return std::pair<bool, bool>{false, false};
+ };
+
+ placeTextForPlacementModes(placeHorizontal, placeVertical);
+
+ placeText = placed.first;
+ offscreen &= placed.second;
+
+ updatePreviousOrientationIfNotPlaced(placed.first);
// If we didn't get placed, we still need to copy our position from the last placement for
// fade animations
- if (prevPlacement && variableOffsets.find(symbolInstance.crossTileID) == variableOffsets.end()) {
+ if (!placeText && prevPlacement) {
auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
if (prevOffset != prevPlacement->variableOffsets.end()) {
variableOffsets[symbolInstance.crossTileID] = prevOffset->second;
- markUsedJustification(bucket, prevOffset->second.anchor, symbolInstance);
}
}
}
}
if (symbolInstance.placedIconIndex) {
- PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
- const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
+ if (!hasIconTextFit || !placeText || variableTextAnchors.empty()) {
+ shift = {0.0f, 0.0f};
+ }
- auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {},
- posMatrix, iconLabelPlaneMatrix, pixelRatio,
- placedSymbol, scale, fontSize,
- layout.get<style::IconAllowOverlap>(),
- pitchWithMap,
- params.showCollisionBoxes, avoidEdges, collisionGroup.second);
- placeIcon = placed.first;
- offscreen &= placed.second;
+ const auto& iconBuffer = symbolInstance.hasSdfIcon() ? bucket.sdfIcon : bucket.icon;
+ const PlacedSymbol& placedSymbol = iconBuffer.placedSymbols.at(*symbolInstance.placedIconIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
+ const auto& placeIconFeature = [&] (const CollisionFeature& collisionFeature) {
+ return collisionIndex.placeFeature(collisionFeature, shift,
+ posMatrix, iconLabelPlaneMatrix, pixelRatio,
+ placedSymbol, scale, fontSize,
+ layout.get<style::IconAllowOverlap>(),
+ pitchWithMap,
+ params.showCollisionBoxes, avoidEdges,
+ collisionGroup.second, iconBoxes);
+ };
+
+ std::pair<bool, bool> placedIcon = {false, false};
+ if (placedVerticalText.first && symbolInstance.verticalIconCollisionFeature) {
+ placedIcon = placedVerticalIcon = placeIconFeature(*symbolInstance.verticalIconCollisionFeature);
+ } else {
+ placedIcon = placeIconFeature(symbolInstance.iconCollisionFeature);
+ }
+ placeIcon = placedIcon.first;
+ offscreen &= placedIcon.second;
}
- const bool iconWithoutText = !symbolInstance.hasText || layout.get<style::TextOptional>();
- const bool textWithoutIcon = !symbolInstance.hasIcon || layout.get<style::IconOptional>();
+ const bool iconWithoutText = !symbolInstance.hasText() || layout.get<style::TextOptional>();
+ const bool textWithoutIcon = !symbolInstance.hasIcon() || layout.get<style::IconOptional>();
// combine placements for icon and text
if (!iconWithoutText && !textWithoutIcon) {
@@ -300,11 +400,29 @@ void Placement::placeBucket(
}
if (placeText) {
- collisionIndex.insertFeature(symbolInstance.textCollisionFeature, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ if (placedVerticalText.first && symbolInstance.verticalTextCollisionFeature) {
+ collisionIndex.insertFeature(*symbolInstance.verticalTextCollisionFeature, textBoxes, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ } else {
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, textBoxes, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ }
}
if (placeIcon) {
- collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ if (placedVerticalIcon.first && symbolInstance.verticalIconCollisionFeature) {
+ collisionIndex.insertFeature(*symbolInstance.verticalIconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ } else {
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
+ }
+ }
+
+ const bool hasIconCollisionCircleData = bucket.hasIconCollisionCircleData();
+ const bool hasTextCollisionCircleData = bucket.hasTextCollisionCircleData();
+
+ if (hasIconCollisionCircleData && symbolInstance.iconCollisionFeature.alongLine && !iconBoxes.empty()) {
+ collisionCircles[&symbolInstance.iconCollisionFeature] = iconBoxes;
+ }
+ if (hasTextCollisionCircleData && symbolInstance.textCollisionFeature.alongLine && !textBoxes.empty()) {
+ collisionCircles[&symbolInstance.textCollisionFeature] = textBoxes;
}
assert(symbolInstance.crossTileID != 0);
@@ -326,7 +444,7 @@ void Placement::placeBucket(
placeSymbol(*it);
}
} else {
- for (SymbolInstance& symbol : bucket.symbolInstances) {
+ for (const SymbolInstance& symbol : bucket.symbolInstances) {
placeSymbol(symbol);
}
}
@@ -340,17 +458,15 @@ void Placement::placeBucket(
std::forward_as_tuple(bucket.bucketInstanceId, params.featureIndex, overscaledID));
}
-void Placement::commit(TimePoint now) {
+void Placement::commit(TimePoint now, const double zoom) {
assert(prevPlacement);
commitTime = now;
bool placementChanged = false;
- float increment = mapMode == MapMode::Continuous &&
- transitionOptions.enablePlacementTransitions &&
- transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) > Milliseconds(0) ?
- std::chrono::duration<float>(commitTime - prevPlacement->commitTime) / transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) :
- 1.0;
+ prevZoomAdjustment = prevPlacement->zoomAdjustment(zoom);
+
+ float increment = prevPlacement->symbolFadeChange(commitTime);
// add the opacities from the current placement, and copy their current values from the previous placement
for (auto& jointPlacement : placements) {
@@ -386,6 +502,15 @@ void Placement::commit(TimePoint now) {
}
}
+ for (auto& prevOrientation : prevPlacement->placedOrientations) {
+ const uint32_t crossTileID = prevOrientation.first;
+ auto foundOrientation = placedOrientations.find(crossTileID);
+ auto foundOpacity = opacities.find(crossTileID);
+ if (foundOrientation == placedOrientations.end() && foundOpacity != opacities.end() && !foundOpacity->second.isHidden()) {
+ placedOrientations[prevOrientation.first] = prevOrientation.second;
+ }
+ }
+
fadeStartTime = placementChanged ? commitTime : prevPlacement->fadeStartTime;
}
@@ -397,30 +522,40 @@ void Placement::updateLayerBuckets(const RenderLayer& layer, const TransformStat
}
namespace {
-Point<float> calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale, float renderTextSize) {
+Point<float> calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, std::array<float, 2> textOffset, float textBoxScale, float renderTextSize) {
AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor);
float shiftX = -(alignment.horizontalAlign - 0.5f) * width;
float shiftY = -(alignment.verticalAlign - 0.5f) * height;
- Point<float> offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset);
- return { (shiftX / textBoxScale + offset.x) * renderTextSize,
- (shiftY / textBoxScale + offset.y) * renderTextSize };
+ auto variablOffset = SymbolLayout::evaluateVariableOffset(anchor, textOffset);
+ return { (shiftX / textBoxScale + variablOffset[0]) * renderTextSize,
+ (shiftY / textBoxScale + variablOffset[1]) * renderTextSize };
}
} // namespace
-bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const TransformState& state, const RenderTile& tile) {
+bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const TransformState& state, const RenderTile& tile) const {
using namespace style;
const auto& layout = *bucket.layout;
const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point;
+ const bool hasVariableAnchors = !layout.get<TextVariableAnchor>().empty() && bucket.hasTextData();
+ const bool updateTextFitIcon = layout.get<IconTextFit>() != IconTextFitType::None && (bucket.allowVerticalPlacement || hasVariableAnchors) && (bucket.hasIconData() || bucket.hasSdfIconData());
bool result = false;
if (alongLine) {
- if (bucket.hasIconData() && layout.get<IconRotationAlignment>() == AlignmentType::Map) {
+ if (layout.get<IconRotationAlignment>() == AlignmentType::Map) {
const bool pitchWithMap = layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map;
const bool keepUpright = layout.get<style::IconKeepUpright>();
- reprojectLineLabels(bucket.icon.dynamicVertices, bucket.icon.placedSymbols,
- tile.matrix, pitchWithMap, true /*rotateWithMap*/, keepUpright,
- tile, *bucket.iconSizeBinder, state);
- result = true;
+ if (bucket.hasSdfIconData()) {
+ reprojectLineLabels(bucket.sdfIcon.dynamicVertices, bucket.sdfIcon.placedSymbols,
+ tile.matrix, pitchWithMap, true /*rotateWithMap*/, keepUpright,
+ tile, *bucket.iconSizeBinder, state);
+ result = true;
+ }
+ if (bucket.hasIconData()) {
+ reprojectLineLabels(bucket.icon.dynamicVertices, bucket.icon.placedSymbols,
+ tile.matrix, pitchWithMap, true /*rotateWithMap*/, keepUpright,
+ tile, *bucket.iconSizeBinder, state);
+ result = true;
+ }
}
if (bucket.hasTextData() && layout.get<TextRotationAlignment>() == AlignmentType::Map) {
@@ -431,7 +566,7 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
tile, *bucket.textSizeBinder, state);
result = true;
}
- } else if (!layout.get<TextVariableAnchor>().empty() && bucket.hasTextData()) {
+ } else if (hasVariableAnchors) {
bucket.text.dynamicVertices.clear();
bucket.hasVariablePlacement = false;
@@ -441,10 +576,13 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
const bool pitchWithMap = layout.get<TextPitchAlignment>() == AlignmentType::Map;
const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom());
const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
+ std::unordered_map<std::size_t, std::pair<std::size_t, Point<float>>> placedTextShifts;
- for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
+ for (std::size_t i = 0; i < bucket.text.placedSymbols.size(); ++i) {
+ const PlacedSymbol& symbol = bucket.text.placedSymbols[i];
optional<VariableOffset> variableOffset;
- if (!symbol.hidden && symbol.crossTileID != 0u) {
+ const bool skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation;
+ if (!symbol.hidden && symbol.crossTileID != 0u && !skipOrientation) {
auto it = variableOffsets.find(symbol.crossTileID);
if (it != variableOffsets.end()) {
bucket.hasVariablePlacement = true;
@@ -470,7 +608,7 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
(*variableOffset).anchor,
(*variableOffset).width,
(*variableOffset).height,
- (*variableOffset).radialOffset,
+ (*variableOffset).offset,
(*variableOffset).textBoxScale,
renderTextSize);
@@ -481,21 +619,69 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
if (pitchWithMap) {
shiftedAnchor = project(Point<float>(tileAnchor.x + shift.x, tileAnchor.y + shift.y),
labelPlaneMatrix).first;
+ } else if (rotateWithMap) {
+ auto rotated = util::rotate(shift, -state.getPitch());
+ shiftedAnchor = Point<float>(projectedAnchor.first.x + rotated.x,
+ projectedAnchor.first.y + rotated.y);
} else {
- if (rotateWithMap) {
- auto rotated = util::rotate(shift, -state.getPitch());
- shiftedAnchor = Point<float>(projectedAnchor.first.x + rotated.x,
- projectedAnchor.first.y + rotated.y);
+ shiftedAnchor = Point<float>(projectedAnchor.first.x + shift.x,
+ projectedAnchor.first.y + shift.y);
+ }
+
+ if (updateTextFitIcon && symbol.placedIconIndex) {
+ placedTextShifts.emplace(*symbol.placedIconIndex,
+ std::pair<std::size_t, Point<float>>{i, shiftedAnchor});
+ }
+
+ for (std::size_t j = 0; j < symbol.glyphOffsets.size(); ++j) {
+ addDynamicAttributes(shiftedAnchor, symbol.angle, bucket.text.dynamicVertices);
+ }
+ }
+ }
+
+ if (updateTextFitIcon && bucket.hasVariablePlacement) {
+ auto updateIcon = [&](SymbolBucket::Buffer& iconBuffer) {
+ iconBuffer.dynamicVertices.clear();
+ for (std::size_t i = 0; i < iconBuffer.placedSymbols.size(); ++i) {
+ const PlacedSymbol& placedIcon = iconBuffer.placedSymbols[i];
+ if (placedIcon.hidden || (!placedIcon.placedOrientation && bucket.allowVerticalPlacement)) {
+ hideGlyphs(placedIcon.glyphOffsets.size(), iconBuffer.dynamicVertices);
} else {
- shiftedAnchor = Point<float>(projectedAnchor.first.x + shift.x,
- projectedAnchor.first.y + shift.y);
+ const auto& pair = placedTextShifts.find(i);
+ if (pair == placedTextShifts.end()) {
+ hideGlyphs(placedIcon.glyphOffsets.size(), iconBuffer.dynamicVertices);
+ } else {
+ for (std::size_t j = 0; j < placedIcon.glyphOffsets.size(); ++j) {
+ addDynamicAttributes(pair->second.second, placedIcon.angle, iconBuffer.dynamicVertices);
+ }
+ }
}
}
+ };
+ updateIcon(bucket.icon);
+ updateIcon(bucket.sdfIcon);
+ }
- for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
- addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices);
+ result = true;
+ } else if (bucket.allowVerticalPlacement && bucket.hasTextData()) {
+ const auto updateDynamicVertices = [](SymbolBucket::Buffer& buffer) {
+ buffer.dynamicVertices.clear();
+ for (const PlacedSymbol& symbol : buffer.placedSymbols) {
+ if (symbol.hidden || !symbol.placedOrientation) {
+ hideGlyphs(symbol.glyphOffsets.size(), buffer.dynamicVertices);
+ } else {
+ for (std::size_t j = 0; j < symbol.glyphOffsets.size(); ++j) {
+ addDynamicAttributes(symbol.anchorPoint, symbol.angle, buffer.dynamicVertices);
+ }
}
}
+ };
+
+ updateDynamicVertices(bucket.text);
+ // When text box is rotated, icon-text-fit icon must be rotated as well.
+ if (updateTextFitIcon) {
+ updateDynamicVertices(bucket.icon);
+ updateDynamicVertices(bucket.sdfIcon);
}
result = true;
@@ -507,23 +693,27 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState& state, std::set<uint32_t>& seenCrossTileIDs) {
if (bucket.hasTextData()) bucket.text.opacityVertices.clear();
if (bucket.hasIconData()) bucket.icon.opacityVertices.clear();
- if (bucket.hasCollisionBoxData()) bucket.collisionBox->dynamicVertices.clear();
- if (bucket.hasCollisionCircleData()) bucket.collisionCircle->dynamicVertices.clear();
+ if (bucket.hasSdfIconData()) bucket.sdfIcon.opacityVertices.clear();
+ if (bucket.hasIconCollisionBoxData()) bucket.iconCollisionBox->dynamicVertices.clear();
+ if (bucket.hasIconCollisionCircleData()) bucket.iconCollisionCircle->dynamicVertices.clear();
+ if (bucket.hasTextCollisionBoxData()) bucket.textCollisionBox->dynamicVertices.clear();
+ if (bucket.hasTextCollisionCircleData()) bucket.textCollisionCircle->dynamicVertices.clear();
- JointOpacityState duplicateOpacityState(false, false, true);
+ const JointOpacityState duplicateOpacityState(false, false, true);
const bool textAllowOverlap = bucket.layout->get<style::TextAllowOverlap>();
const bool iconAllowOverlap = bucket.layout->get<style::IconAllowOverlap>();
const bool variablePlacement = !bucket.layout->get<style::TextVariableAnchor>().empty();
const bool rotateWithMap = bucket.layout->get<style::TextRotationAlignment>() == style::AlignmentType::Map;
const bool pitchWithMap = bucket.layout->get<style::TextPitchAlignment>() == style::AlignmentType::Map;
+ const bool hasIconTextFit = bucket.layout->get<style::IconTextFit>() != style::IconTextFitType::None;
// If allow-overlap is true, we can show symbols before placement runs on them
// But we have to wait for placement if we potentially depend on a paired icon/text
// with allow-overlap: false.
// See https://github.com/mapbox/mapbox-gl-native/issues/12483
- JointOpacityState defaultOpacityState(
- textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || bucket.layout->get<style::IconOptional>()),
+ const JointOpacityState defaultOpacityState(
+ textAllowOverlap && (iconAllowOverlap || !(bucket.hasIconData() || bucket.hasSdfIconData()) || bucket.layout->get<style::IconOptional>()),
iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || bucket.layout->get<style::TextOptional>()),
true);
@@ -538,13 +728,9 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
opacityState = it->second;
}
- if (it == opacities.end()) {
- opacities.emplace(symbolInstance.crossTileID, defaultOpacityState);
- }
-
seenCrossTileIDs.insert(symbolInstance.crossTileID);
- if (symbolInstance.hasText) {
+ if (symbolInstance.hasText()) {
size_t textOpacityVerticesSize = 0u;
const auto& opacityVertex = SymbolSDFTextProgram::opacityVertex(opacityState.text.placed, opacityState.text.opacity);
if (symbolInstance.placedRightTextIndex) {
@@ -569,32 +755,48 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
bucket.text.opacityVertices.extend(textOpacityVerticesSize, opacityVertex);
+ style::TextWritingModeType previousOrientation = style::TextWritingModeType::Horizontal;
+ if (bucket.allowVerticalPlacement) {
+ auto prevOrientation = placedOrientations.find(symbolInstance.crossTileID);
+ if (prevOrientation != placedOrientations.end()) {
+ previousOrientation = prevOrientation->second;
+ markUsedOrientation(bucket, prevOrientation->second, symbolInstance);
+ }
+ }
+
auto prevOffset = variableOffsets.find(symbolInstance.crossTileID);
if (prevOffset != variableOffsets.end()) {
- markUsedJustification(bucket, prevOffset->second.anchor, symbolInstance);
+ markUsedJustification(bucket, prevOffset->second.anchor, symbolInstance, previousOrientation);
}
}
- if (symbolInstance.hasIcon) {
+ if (symbolInstance.hasIcon()) {
const auto& opacityVertex = SymbolIconProgram::opacityVertex(opacityState.icon.placed, opacityState.icon.opacity);
- bucket.icon.opacityVertices.extend(4, opacityVertex);
+ auto& iconBuffer = symbolInstance.hasSdfIcon() ? bucket.sdfIcon : bucket.icon;
+
if (symbolInstance.placedIconIndex) {
- bucket.icon.placedSymbols[*symbolInstance.placedIconIndex].hidden = opacityState.isHidden();
+ iconBuffer.opacityVertices.extend(4, opacityVertex);
+ iconBuffer.placedSymbols[*symbolInstance.placedIconIndex].hidden = opacityState.isHidden();
+ }
+
+ if (symbolInstance.placedVerticalIconIndex) {
+ iconBuffer.opacityVertices.extend(4, opacityVertex);
+ iconBuffer.placedSymbols[*symbolInstance.placedVerticalIconIndex].hidden = opacityState.isHidden();
}
}
- auto updateCollisionBox = [&](const auto& feature, const bool placed) {
+ auto updateIconCollisionBox = [&](const auto& feature, const bool placed, const Point<float>& shift) {
if (feature.alongLine) {
return;
}
- const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false, {});
- bucket.collisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
+ const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false, shift);
+ bucket.iconCollisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
};
- auto updateCollisionTextBox = [this, &bucket, &symbolInstance, &state, variablePlacement, rotateWithMap, pitchWithMap](const auto& feature, const bool placed) {
+ auto updateTextCollisionBox = [this, &bucket, &symbolInstance, &state, variablePlacement, rotateWithMap, pitchWithMap](const auto& feature, const bool placed) {
+ Point<float> shift{0.0f, 0.0f};
if (feature.alongLine) {
- return;
+ return shift;
}
- Point<float> shift;
bool used = true;
if (variablePlacement) {
auto foundOffset = variableOffsets.find(symbolInstance.crossTileID);
@@ -607,7 +809,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
shift = calculateVariableLayoutOffset(variableOffset.anchor,
variableOffset.width,
variableOffset.height,
- variableOffset.radialOffset,
+ variableOffset.offset,
variableOffset.textBoxScale);
if (rotateWithMap) {
shift = util::rotate(shift, pitchWithMap ? state.getBearing() : -state.getBearing());
@@ -620,26 +822,48 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
}
}
const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !used, shift);
- bucket.collisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
+ bucket.textCollisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
+ return shift;
};
- auto updateCollisionCircles = [&](const auto& feature, const bool placed) {
+ auto updateCollisionCircles = [&](const auto& feature, const bool placed, bool isText) {
if (!feature.alongLine) {
return;
}
- for (const CollisionBox& box : feature.boxes) {
- const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !box.used, {});
- bucket.collisionCircle->dynamicVertices.extend(4, dynamicVertex);
+ auto circles = collisionCircles.find(&feature);
+ if (circles != collisionCircles.end()) {
+ for (const auto& circle : circles->second) {
+ const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !circle.isCircle(), {});
+ isText ? bucket.textCollisionCircle->dynamicVertices.extend(4, dynamicVertex):
+ bucket.iconCollisionCircle->dynamicVertices.extend(4, dynamicVertex);
+ }
+ } else {
+ // This feature was not placed, because it was not loaded or from a fading tile. Apply default values.
+ static const auto dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false /*not used*/, {});
+ isText ? bucket.textCollisionCircle->dynamicVertices.extend(4 * feature.boxes.size(), dynamicVertex):
+ bucket.iconCollisionCircle->dynamicVertices.extend(4 * feature.boxes.size(), dynamicVertex);
}
};
-
- if (bucket.hasCollisionBoxData()) {
- updateCollisionTextBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
- updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
+ Point<float> textShift{0.0f, 0.0f};
+ Point<float> verticalTextShift{0.0f, 0.0f};
+ if (bucket.hasTextCollisionBoxData()) {
+ textShift = updateTextCollisionBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
+ if (bucket.allowVerticalPlacement && symbolInstance.verticalTextCollisionFeature) {
+ verticalTextShift = updateTextCollisionBox(*symbolInstance.verticalTextCollisionFeature, opacityState.text.placed);
+ }
+ }
+ if (bucket.hasIconCollisionBoxData()) {
+ updateIconCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed, hasIconTextFit ? textShift : Point<float>{0.0f, 0.0f});
+ if (bucket.allowVerticalPlacement && symbolInstance.verticalIconCollisionFeature) {
+ updateIconCollisionBox(*symbolInstance.verticalIconCollisionFeature, opacityState.text.placed, hasIconTextFit ? verticalTextShift : Point<float>{0.0f, 0.0f});
+ }
+ }
+
+ if (bucket.hasIconCollisionCircleData()) {
+ updateCollisionCircles(symbolInstance.iconCollisionFeature, opacityState.icon.placed, false);
}
- if (bucket.hasCollisionCircleData()) {
- updateCollisionCircles(symbolInstance.textCollisionFeature, opacityState.text.placed);
- updateCollisionCircles(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
+ if (bucket.hasTextCollisionCircleData()) {
+ updateCollisionCircles(symbolInstance.textCollisionFeature, opacityState.text.placed, true);
}
}
@@ -651,7 +875,12 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
}
namespace {
-optional<size_t> justificationToIndex(style::TextJustifyType justify, const SymbolInstance& symbolInstance) {
+optional<size_t> justificationToIndex(style::TextJustifyType justify, const SymbolInstance& symbolInstance, style::TextWritingModeType orientation) {
+ // Vertical symbol has just one justification, style::TextJustifyType::Left.
+ if (orientation == style::TextWritingModeType::Vertical) {
+ return symbolInstance.placedVerticalTextIndex;
+ }
+
switch(justify) {
case style::TextJustifyType::Right: return symbolInstance.placedRightTextIndex;
case style::TextJustifyType::Center: return symbolInstance.placedCenterTextIndex;
@@ -666,13 +895,13 @@ const style::TextJustifyType justifyTypes[] = {style::TextJustifyType::Right, st
} // namespace
-void Placement::markUsedJustification(SymbolBucket& bucket, style::TextVariableAnchorType placedAnchor, SymbolInstance& symbolInstance) {
+void Placement::markUsedJustification(SymbolBucket& bucket, style::TextVariableAnchorType placedAnchor, const SymbolInstance& symbolInstance, style::TextWritingModeType orientation) {
style::TextJustifyType anchorJustify = getAnchorJustification(placedAnchor);
assert(anchorJustify != style::TextJustifyType::Auto);
- const optional<size_t>& autoIndex = justificationToIndex(anchorJustify, symbolInstance);
+ const optional<size_t>& autoIndex = justificationToIndex(anchorJustify, symbolInstance, orientation);
for (auto& justify : justifyTypes) {
- const optional<size_t> index = justificationToIndex(justify, symbolInstance);
+ const optional<size_t> index = justificationToIndex(justify, symbolInstance, orientation);
if (index) {
assert(bucket.text.placedSymbols.size() > *index);
if (autoIndex && *index != *autoIndex) {
@@ -686,15 +915,66 @@ void Placement::markUsedJustification(SymbolBucket& bucket, style::TextVariableA
}
}
+void Placement::markUsedOrientation(SymbolBucket& bucket, style::TextWritingModeType orientation, const SymbolInstance& symbolInstance) {
+ auto horizontal = orientation == style::TextWritingModeType::Horizontal ?
+ optional<style::TextWritingModeType>(orientation) : nullopt;
+ auto vertical = orientation == style::TextWritingModeType::Vertical ?
+ optional<style::TextWritingModeType>(orientation) : nullopt;
+
+ if (symbolInstance.placedRightTextIndex) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedRightTextIndex).placedOrientation = horizontal;
+ }
+
+ if (symbolInstance.placedCenterTextIndex && !symbolInstance.singleLine) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedCenterTextIndex).placedOrientation = horizontal;
+ }
+
+ if (symbolInstance.placedLeftTextIndex && !symbolInstance.singleLine) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedLeftTextIndex).placedOrientation = horizontal;
+ }
+
+ if (symbolInstance.placedVerticalTextIndex) {
+ bucket.text.placedSymbols.at(*symbolInstance.placedVerticalTextIndex).placedOrientation = vertical;
+ }
+
+ auto& iconBuffer = symbolInstance.hasSdfIcon() ? bucket.sdfIcon : bucket.icon;
+ if (symbolInstance.placedIconIndex) {
+ iconBuffer.placedSymbols.at(*symbolInstance.placedIconIndex).placedOrientation = horizontal;
+ }
+
+ if (symbolInstance.placedVerticalIconIndex) {
+ iconBuffer.placedSymbols.at(*symbolInstance.placedVerticalIconIndex).placedOrientation = vertical;
+ }
+}
+
float Placement::symbolFadeChange(TimePoint now) const {
if (mapMode == MapMode::Continuous && transitionOptions.enablePlacementTransitions &&
transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) > Milliseconds(0)) {
- return std::chrono::duration<float>(now - commitTime) / transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION);
+ return std::chrono::duration<float>(now - commitTime) / transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) + prevZoomAdjustment;
} else {
return 1.0;
}
}
+float Placement::zoomAdjustment(const float zoom) const {
+ // When zooming out labels can overlap each other quickly. This
+ // adjustment is used to reduce the fade duration for symbols while zooming out quickly.
+ // It is also used to reduce the interval between placement calculations. Reducing the
+ // interval between placements means collisions are discovered and eliminated sooner.
+ return std::max(0.0, (placementZoom - zoom) / 1.5);
+}
+
+Duration Placement::getUpdatePeriod(const float zoom) const {
+ // Even if transitionOptions.duration is set to a value < 300ms, we still wait for this default transition duration
+ // before attempting another placement operation.
+ const auto fadeDuration = std::max(util::DEFAULT_TRANSITION_DURATION, transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION));
+ const auto adjustedDuration = std::chrono::duration_cast<Duration>(fadeDuration * (1.0 - zoomAdjustment(zoom)));
+ if (maximumUpdatePeriod) {
+ return std::min(*maximumUpdatePeriod, adjustedDuration);
+ }
+ return adjustedDuration;
+}
+
bool Placement::hasTransitions(TimePoint now) const {
if (mapMode == MapMode::Continuous && transitionOptions.enablePlacementTransitions) {
return stale || std::chrono::duration<float>(now - fadeStartTime) < transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION);
@@ -703,12 +983,14 @@ bool Placement::hasTransitions(TimePoint now) const {
}
}
-bool Placement::stillRecent(TimePoint now) const {
- // Even if transitionOptions.duration is set to a value < 300ms, we still wait for this default transition duration
- // before attempting another placement operation.
+bool Placement::stillRecent(TimePoint now, const float zoom) const {
return mapMode == MapMode::Continuous &&
transitionOptions.enablePlacementTransitions &&
- commitTime + std::max(util::DEFAULT_TRANSITION_DURATION, transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION)) > now;
+ commitTime + getUpdatePeriod(zoom) > now;
+}
+
+void Placement::setMaximumUpdatePeriod(Duration duration) {
+ maximumUpdatePeriod = duration;
}
void Placement::setStale() {
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
index 0f56b0007e..b5405cbcd7 100644
--- a/src/mbgl/text/placement.hpp
+++ b/src/mbgl/text/placement.hpp
@@ -12,6 +12,7 @@ namespace mbgl {
class SymbolBucket;
class SymbolInstance;
+enum class PlacedSymbolOrientation : bool;
class OpacityState {
public:
@@ -33,7 +34,7 @@ public:
class VariableOffset {
public:
- float radialOffset;
+ std::array<float, 2> offset;
float width;
float height;
style::TextVariableAnchorType anchor;
@@ -101,28 +102,31 @@ class Placement {
public:
Placement(const TransformState&, MapMode, style::TransitionOptions, const bool crossSourceCollisions, std::unique_ptr<Placement> prevPlacementOrNull = nullptr);
void placeLayer(const RenderLayer&, const mat4&, bool showCollisionBoxes);
- void commit(TimePoint);
+ void commit(TimePoint, const double zoom);
void updateLayerBuckets(const RenderLayer&, const TransformState&, bool updateOpacities);
float symbolFadeChange(TimePoint now) const;
bool hasTransitions(TimePoint now) const;
const CollisionIndex& getCollisionIndex() const;
- bool stillRecent(TimePoint now) const;
- void setRecent(TimePoint now);
+ bool stillRecent(TimePoint now, const float zoom) const;
+ void setMaximumUpdatePeriod(Duration);
void setStale();
const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const;
private:
friend SymbolBucket;
void placeBucket(
- SymbolBucket&,
+ const SymbolBucket&,
const BucketPlacementParameters&,
std::set<uint32_t>& seenCrossTileIDs);
// Returns `true` if bucket vertices were updated; returns `false` otherwise.
- bool updateBucketDynamicVertices(SymbolBucket&, const TransformState&, const RenderTile& tile);
+ bool updateBucketDynamicVertices(SymbolBucket&, const TransformState&, const RenderTile& tile) const;
void updateBucketOpacities(SymbolBucket&, const TransformState&, std::set<uint32_t>&);
- void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, SymbolInstance&);
+ void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, const SymbolInstance&, style::TextWritingModeType orientation);
+ void markUsedOrientation(SymbolBucket&, style::TextWritingModeType, const SymbolInstance&);
+ float zoomAdjustment(const float zoom) const;
+ Duration getUpdatePeriod(const float zoom) const;
CollisionIndex collisionIndex;
@@ -131,16 +135,23 @@ private:
TimePoint fadeStartTime;
TimePoint commitTime;
+ float placementZoom;
+ float prevZoomAdjustment = 0;
std::unordered_map<uint32_t, JointPlacement> placements;
std::unordered_map<uint32_t, JointOpacityState> opacities;
std::unordered_map<uint32_t, VariableOffset> variableOffsets;
+ std::unordered_map<uint32_t, style::TextWritingModeType> placedOrientations;
bool stale = false;
std::unordered_map<uint32_t, RetainedQueryData> retainedQueryData;
CollisionGroups collisionGroups;
std::unique_ptr<Placement> prevPlacement;
+ optional<Duration> maximumUpdatePeriod;
+
+ // Used for debug purposes.
+ std::unordered_map<const CollisionFeature*, std::vector<ProjectedCollisionBox>> collisionCircles;
};
} // namespace mbgl
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index 6be5d8c01e..281c5d99de 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -14,9 +14,7 @@ namespace mbgl {
using namespace style;
SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
- const SymbolLayoutProperties::Evaluated& layout,
- const float layoutTextSize,
- const Shaping& shapedText) {
+ WritingModeType writingMode) {
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
@@ -28,43 +26,11 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
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;
- Point<float> bl;
-
- if (layout.get<IconTextFit>() != IconTextFitType::None && shapedText) {
- auto iconWidth = right - left;
- auto iconHeight = bottom - top;
- auto size = layoutTextSize / 24.0f;
- auto textLeft = shapedText.left * size;
- auto textRight = shapedText.right * size;
- auto textTop = shapedText.top * size;
- auto textBottom = shapedText.bottom * size;
- auto textWidth = textRight - textLeft;
- auto textHeight = textBottom - textTop;
- auto padT = layout.get<IconTextFitPadding>()[0];
- auto padR = layout.get<IconTextFitPadding>()[1];
- auto padB = layout.get<IconTextFitPadding>()[2];
- auto padL = layout.get<IconTextFitPadding>()[3];
- auto offsetY = layout.get<IconTextFit>() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0;
- auto offsetX = layout.get<IconTextFit>() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0;
- auto width = layout.get<IconTextFit>() == IconTextFitType::Width || layout.get<IconTextFit>() == IconTextFitType::Both ? textWidth : iconWidth;
- auto height = layout.get<IconTextFit>() == IconTextFitType::Height || layout.get<IconTextFit>() == IconTextFitType::Both ? textHeight : iconHeight;
- left = textLeft + offsetX - padL;
- top = textTop + offsetY - padT;
- right = textLeft + offsetX + padR + width;
- bottom = textTop + offsetY + padB + height;
- tl = {left, top};
- tr = {right, top};
- br = {right, bottom};
- bl = {left, bottom};
- } else {
- tl = {left, top};
- tr = {right, top};
- br = {right, bottom};
- bl = {left, bottom};
- }
+
+ Point<float> tl{left, top};
+ Point<float> tr{right, top};
+ Point<float> br{right, bottom};
+ Point<float> bl{left, bottom};
const float angle = shapedIcon.angle();
@@ -88,15 +54,17 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
static_cast<uint16_t>(image.textureRect.h + border * 2)
};
- return SymbolQuad { tl, tr, bl, br, textureRect, shapedText.writingMode, { 0.0f, 0.0f } };
+ return SymbolQuad { tl, tr, bl, br, textureRect, writingMode, { 0.0f, 0.0f } };
}
SymbolQuads getGlyphQuads(const Shaping& shapedText,
const std::array<float, 2> textOffset,
const SymbolLayoutProperties::Evaluated& layout,
const style::SymbolPlacementType placement,
- const GlyphPositions& positions) {
+ const GlyphPositions& positions,
+ bool allowVerticalPlacement) {
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
+ const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement != SymbolPlacementType::Point;
SymbolQuads quads;
@@ -117,16 +85,23 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
const float rectBuffer = 3.0f + glyphPadding;
const float halfAdvance = glyph.metrics.advance * positionedGlyph.scale / 2.0;
- const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement != SymbolPlacementType::Point;
const Point<float> glyphOffset = alongLine ?
Point<float>{ positionedGlyph.x + halfAdvance, positionedGlyph.y } :
Point<float>{ 0.0f, 0.0f };
- const Point<float> builtInOffset = alongLine ?
+ Point<float> builtInOffset = alongLine ?
Point<float>{ 0.0f, 0.0f } :
Point<float>{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] };
+ Point<float> verticalizedLabelOffset = { 0.0f, 0.0f };
+ const bool rotateVerticalGlyph = (alongLine || allowVerticalPlacement) && positionedGlyph.vertical;
+ if (rotateVerticalGlyph) {
+ // Vertical POI labels, that are rotated 90deg CW and whose glyphs must preserve upright orientation
+ // need to be rotated 90deg CCW. After quad is rotated, it is translated to the original built-in offset.
+ verticalizedLabelOffset = builtInOffset;
+ builtInOffset = { 0.0f, 0.0f };
+ }
const float x1 = (glyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset.x;
const float y1 = (-glyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset.y;
@@ -138,22 +113,29 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
Point<float> bl{x1, y2};
Point<float> br{x2, y2};
- if (alongLine && positionedGlyph.vertical) {
+ if (rotateVerticalGlyph) {
// 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};
+ // necessary, but we also pull the glyph to the left along the x axis.
+ // The y coordinate includes baseline yOffset, therefore, needs to be accounted
+ // for when glyph is rotated and translated.
+
+ const Point<float> center{ -halfAdvance, halfAdvance - Shaping::yOffset };
const float verticalRotation = -M_PI_2;
- const Point<float> xOffsetCorrection{5, 0};
+
+ // xHalfWidhtOffsetcorrection is a difference between full-width and half-width
+ // advance, should be 0 for full-width glyphs and will pull up half-width glyphs.
+ const float xHalfWidhtOffsetcorrection = util::ONE_EM / 2 - halfAdvance;
+ const Point<float> xOffsetCorrection{ 5.0f - Shaping::yOffset - xHalfWidhtOffsetcorrection, 0.0f };
- 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;
+ tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
+ tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
+ bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
+ br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
}
if (textRotate) {
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index 0bb892e4d1..145fd2b153 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -44,14 +44,13 @@ public:
using SymbolQuads = std::vector<SymbolQuad>;
SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
- const style::SymbolLayoutProperties::Evaluated&,
- const float layoutTextSize,
- const Shaping& shapedText);
+ WritingModeType writingMode);
SymbolQuads getGlyphQuads(const Shaping& shapedText,
const std::array<float, 2> textOffset,
const style::SymbolLayoutProperties::Evaluated&,
style::SymbolPlacementType placement,
- const GlyphPositions& positions);
+ const GlyphPositions& positions,
+ bool allowVerticalPlacement);
} // namespace mbgl
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 1d95376b04..d6d9a3d34e 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -68,16 +68,49 @@ style::TextJustifyType getAnchorJustification(style::SymbolAnchorType anchor) {
}
}
-PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) {
+PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image,
+ const std::array<float, 2>& iconOffset,
+ style::SymbolAnchorType iconAnchor,
+ const float iconRotation) {
AnchorAlignment anchorAlign = AnchorAlignment::getAnchorAlignment(iconAnchor);
float dx = iconOffset[0];
float dy = iconOffset[1];
- float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
- float x2 = x1 + image.displaySize()[0];
- float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
- float y2 = y1 + image.displaySize()[1];
+ float left = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
+ float right = left + image.displaySize()[0];
+ float top = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
+ float bottom = top + image.displaySize()[1];
- return PositionedIcon { image, y1, y2, x1, x2, iconRotation };
+ return PositionedIcon { image, top, bottom, left, right, iconRotation };
+}
+
+void PositionedIcon::fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout,
+ const Shaping& shapedText,
+ float layoutTextSize) {
+ using namespace style;
+ assert(layout.get<IconTextFit>() != IconTextFitType::None);
+ if (shapedText) {
+ auto iconWidth = _right - _left;
+ auto iconHeight = _bottom - _top;
+ auto size = layoutTextSize / 24.0f;
+ auto textLeft = shapedText.left * size;
+ auto textRight = shapedText.right * size;
+ auto textTop = shapedText.top * size;
+ auto textBottom = shapedText.bottom * size;
+ auto textWidth = textRight - textLeft;
+ auto textHeight = textBottom - textTop;
+ auto padT = layout.get<IconTextFitPadding>()[0];
+ auto padR = layout.get<IconTextFitPadding>()[1];
+ auto padB = layout.get<IconTextFitPadding>()[2];
+ auto padL = layout.get<IconTextFitPadding>()[3];
+ auto offsetY = layout.get<IconTextFit>() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0;
+ auto offsetX = layout.get<IconTextFit>() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0;
+ auto width = layout.get<IconTextFit>() == IconTextFitType::Width || layout.get<IconTextFit>() == IconTextFitType::Both ? textWidth : iconWidth;
+ auto height = layout.get<IconTextFit>() == IconTextFitType::Height || layout.get<IconTextFit>() == IconTextFitType::Both ? textHeight : iconHeight;
+ _left = textLeft + offsetX - padL;
+ _top = textTop + offsetY - padT;
+ _right = textLeft + offsetX + padR + width;
+ _bottom = textTop + offsetY + padB + height;
+ }
}
void align(Shaping& shaping,
@@ -238,9 +271,8 @@ std::set<std::size_t> leastBadBreaks(const PotentialBreak& lastLineBreak) {
std::set<std::size_t> determineLineBreaks(const TaggedString& logicalInput,
const float spacing,
float maxWidth,
- const WritingModeType writingMode,
const GlyphMap& glyphMap) {
- if (!maxWidth || writingMode != WritingModeType::Horizontal) {
+ if (!maxWidth) {
return {};
}
@@ -291,13 +323,10 @@ void shapeLines(Shaping& shaping,
const style::SymbolAnchorType textAnchor,
const style::TextJustifyType textJustify,
const WritingModeType writingMode,
- const GlyphMap& glyphMap) {
-
- // the y offset *should* be part of the font metadata
- const int32_t yOffset = -17;
-
+ const GlyphMap& glyphMap,
+ bool allowVerticalPlacement) {
float x = 0;
- float y = yOffset;
+ float y = Shaping::yOffset;
float maxLineLength = 0;
@@ -337,12 +366,17 @@ void shapeLines(Shaping& shaping,
const double baselineOffset = (lineMaxScale - section.scale) * util::ONE_EM;
const Glyph& glyph = **it->second;
-
- if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(codePoint)) {
+
+ if (writingMode == WritingModeType::Horizontal ||
+ // Don't verticalize glyphs that have no upright orientation if vertical placement is disabled.
+ (!allowVerticalPlacement && !util::i18n::hasUprightVerticalOrientation(codePoint)) ||
+ // If vertical placement is ebabled, don't verticalize glyphs that
+ // are from complex text layout script, or whitespaces.
+ (allowVerticalPlacement && (util::i18n::isWhitespace(codePoint) || util::i18n::isCharInComplexShapingScript(codePoint)))) {
shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale, sectionIndex);
x += glyph.metrics.advance * section.scale + spacing;
} else {
- shaping.positionedGlyphs.emplace_back(codePoint, x, baselineOffset, true, section.fontStackHash, section.scale, sectionIndex);
+ shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, true, section.fontStackHash, section.scale, sectionIndex);
x += util::ONE_EM * section.scale + spacing;
}
}
@@ -364,7 +398,7 @@ void shapeLines(Shaping& shaping,
align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
lineHeight, lines.size());
- const float height = y - yOffset;
+ const float height = y - Shaping::yOffset;
// Calculate the bounding box
shaping.top += -anchorAlign.verticalAlign * height;
@@ -379,27 +413,28 @@ const Shaping getShaping(const TaggedString& formattedString,
const style::SymbolAnchorType textAnchor,
const style::TextJustifyType textJustify,
const float spacing,
- const Point<float>& translate,
+ const std::array<float, 2>& translate,
const WritingModeType writingMode,
BiDi& bidi,
- const GlyphMap& glyphs) {
+ const GlyphMap& glyphs,
+ bool allowVerticalPlacement) {
std::vector<TaggedString> reorderedLines;
if (formattedString.sectionCount() == 1) {
auto untaggedLines = bidi.processText(formattedString.rawText(),
- determineLineBreaks(formattedString, spacing, maxWidth, writingMode, glyphs));
+ determineLineBreaks(formattedString, spacing, maxWidth, glyphs));
for (const auto& line : untaggedLines) {
reorderedLines.emplace_back(line, formattedString.sectionAt(0));
}
} else {
auto processedLines = bidi.processStyledText(formattedString.getStyledText(),
- determineLineBreaks(formattedString, spacing, maxWidth, writingMode, glyphs));
+ determineLineBreaks(formattedString, spacing, maxWidth, glyphs));
for (const auto& line : processedLines) {
reorderedLines.emplace_back(line, formattedString.getSections());
}
}
- Shaping shaping(translate.x, translate.y, writingMode, reorderedLines.size());
+ Shaping shaping(translate[0], translate[1], writingMode, reorderedLines.size());
shapeLines(shaping, reorderedLines, spacing, lineHeight, textAnchor,
- textJustify, writingMode, glyphs);
+ textJustify, writingMode, glyphs, allowVerticalPlacement);
return shaping;
}
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index 766b1ce233..0e2f5515fe 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -4,6 +4,7 @@
#include <mbgl/text/tagged_string.hpp>
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/style/types.hpp>
+#include <mbgl/style/layers/symbol_layer_properties.hpp>
namespace mbgl {
@@ -52,6 +53,12 @@ public:
style::SymbolAnchorType iconAnchor,
const float iconRotation);
+ // Updates shaped icon's bounds based on shaped text's bounds and provided
+ // layout properties.
+ void fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout,
+ const Shaping& shapedText,
+ float layoutTextSize);
+
const ImagePosition& image() const { return _image; }
float top() const { return _top; }
float bottom() const { return _bottom; }
@@ -66,9 +73,10 @@ const Shaping getShaping(const TaggedString& string,
style::SymbolAnchorType textAnchor,
style::TextJustifyType textJustify,
float spacing,
- const Point<float>& translate,
+ const std::array<float, 2>& translate,
const WritingModeType,
BiDi& bidi,
- const GlyphMap& glyphs);
+ const GlyphMap& glyphs,
+ bool allowVerticalPlacement);
} // namespace mbgl
diff --git a/src/mbgl/text/tagged_string.cpp b/src/mbgl/text/tagged_string.cpp
index 8c4e3b02e8..e8a1c6f51f 100644
--- a/src/mbgl/text/tagged_string.cpp
+++ b/src/mbgl/text/tagged_string.cpp
@@ -8,6 +8,7 @@ void TaggedString::addSection(const std::u16string& sectionText, double scale, F
styledText.first += sectionText;
sections.emplace_back(scale, fontStack, std::move(textColor));
styledText.second.resize(styledText.first.size(), sections.size() - 1);
+ supportsVerticalWritingMode = nullopt;
}
void TaggedString::trim() {
@@ -37,4 +38,11 @@ void TaggedString::verticalizePunctuation() {
styledText.first = util::i18n::verticalizePunctuation(styledText.first);
}
+bool TaggedString::allowsVerticalWritingMode() {
+ if (!supportsVerticalWritingMode) {
+ supportsVerticalWritingMode = util::i18n::allowsVerticalWritingMode(rawText());
+ }
+ return *supportsVerticalWritingMode;
+}
+
} // namespace mbgl
diff --git a/src/mbgl/text/tagged_string.hpp b/src/mbgl/text/tagged_string.hpp
index 2607e10889..698e539a45 100644
--- a/src/mbgl/text/tagged_string.hpp
+++ b/src/mbgl/text/tagged_string.hpp
@@ -98,10 +98,12 @@ struct TaggedString {
void trim();
void verticalizePunctuation();
+ bool allowsVerticalWritingMode();
private:
StyledText styledText;
std::vector<SectionOptions> sections;
+ optional<bool> supportsVerticalWritingMode;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index 4cf971df84..0782f74d5d 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -13,8 +13,8 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
updateData(std::move(features));
}
-void GeoJSONTile::updateData(mapbox::feature::feature_collection<int16_t> features) {
- setData(std::make_unique<GeoJSONTileData>(std::move(features)));
+void GeoJSONTile::updateData(mapbox::feature::feature_collection<int16_t> features, bool resetLayers) {
+ setData(std::make_unique<GeoJSONTileData>(std::move(features)), resetLayers);
}
void GeoJSONTile::querySourceFeatures(
diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp
index 725dc4850c..9161e33f0c 100644
--- a/src/mbgl/tile/geojson_tile.hpp
+++ b/src/mbgl/tile/geojson_tile.hpp
@@ -14,7 +14,7 @@ public:
const TileParameters&,
mapbox::feature::feature_collection<int16_t>);
- void updateData(mapbox::feature::feature_collection<int16_t>);
+ void updateData(mapbox::feature::feature_collection<int16_t>, bool resetLayers = false);
void querySourceFeatures(
std::vector<Feature>& result,
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index a431ae423e..3087b4fc6a 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -176,13 +176,13 @@ void GeometryTile::setError(std::exception_ptr err) {
observer->onTileError(*this, err);
}
-void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
+void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_, bool resetLayers) {
// Mark the tile as pending again if it was complete before to prevent signaling a complete
// state despite pending parse operations.
pending = true;
++correlationID;
- worker.self().invoke(&GeometryTileWorker::setData, std::move(data_), correlationID);
+ worker.self().invoke(&GeometryTileWorker::setData, std::move(data_), resetLayers, correlationID);
}
std::unique_ptr<TileRenderData> GeometryTile::createRenderData() {
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index 7c46edfc1d..8682c8c76b 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -34,7 +34,7 @@ public:
~GeometryTile() override;
void setError(std::exception_ptr);
- void setData(std::unique_ptr<const GeometryTileData>);
+ void setData(std::unique_ptr<const GeometryTileData>, bool resetLayers = false);
std::unique_ptr<TileRenderData> createRenderData() override;
void setLayers(const std::vector<Immutable<style::LayerProperties>>&) override;
diff --git a/src/mbgl/tile/geometry_tile_data.cpp b/src/mbgl/tile/geometry_tile_data.cpp
index 5320df6893..f723a94800 100644
--- a/src/mbgl/tile/geometry_tile_data.cpp
+++ b/src/mbgl/tile/geometry_tile_data.cpp
@@ -74,6 +74,7 @@ std::vector<GeometryCollection> classifyRings(const GeometryCollection& rings) {
if (ccw == (area < 0 ? -1 : 1) && !polygon.empty()) {
polygons.emplace_back(std::move(polygon));
+ polygon = GeometryCollection();
}
polygon.emplace_back(ring);
@@ -100,8 +101,8 @@ void limitHoles(GeometryCollection& polygon, uint32_t maxHoles) {
static Feature::geometry_type convertGeometry(const GeometryTileFeature& geometryTileFeature, const CanonicalTileID& tileID) {
const double size = util::EXTENT * std::pow(2, tileID.z);
- const double x0 = util::EXTENT * tileID.x;
- const double y0 = util::EXTENT * tileID.y;
+ const double x0 = util::EXTENT * static_cast<double>(tileID.x);
+ const double y0 = util::EXTENT * static_cast<double>(tileID.y);
auto tileCoordinatesToLatLng = [&] (const Point<int16_t>& p) {
double y2 = 180 - (p.y + y0) * 360 / size;
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index f4d57e5bfc..428a5b0d5e 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -117,10 +117,11 @@ GeometryTileWorker::~GeometryTileWorker() = default;
completed parse.
*/
-void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, uint64_t correlationID_) {
+void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, bool resetLayers_, uint64_t correlationID_) {
try {
data = std::move(data_);
correlationID = correlationID_;
+ if (resetLayers_) layers = nullopt;
switch (state) {
case Idle:
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 258f2cd186..96b4e2e83a 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -39,7 +39,7 @@ public:
~GeometryTileWorker();
void setLayers(std::vector<Immutable<style::LayerProperties>>, uint64_t correlationID);
- void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID);
+ void setData(std::unique_ptr<const GeometryTileData>, bool resetLayers, uint64_t correlationID);
void setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_);
void onGlyphsAvailable(GlyphMap glyphs);
diff --git a/src/mbgl/util/chrono.cpp b/src/mbgl/util/chrono.cpp
index 6674e73c42..03e97eedf5 100644
--- a/src/mbgl/util/chrono.cpp
+++ b/src/mbgl/util/chrono.cpp
@@ -22,8 +22,13 @@ std::string rfc1123(Timestamp timestamp) {
std::time_t time = std::chrono::system_clock::to_time_t(timestamp);
std::tm 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,
+
+ // Buffer size 30 is OK assuming the year has 4 digits. However, In theory, it might have
+ // more digits. Under gcc 8.3.0 with -Os optimization flag, there is compiler warning
+ // complaining about the buffer size might be too small. Inceasing the buffer to 32 fixes
+ // the warning.
+ char buffer[32];
+ snprintf(buffer, 32, "%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);
return buffer;
}
diff --git a/src/mbgl/util/color.cpp b/src/mbgl/util/color.cpp
index b8574174f1..4c2814cf14 100644
--- a/src/mbgl/util/color.cpp
+++ b/src/mbgl/util/color.cpp
@@ -44,4 +44,11 @@ std::array<double, 4> Color::toArray() const {
}
}
+mbgl::Value Color::toObject() const {
+ return std::unordered_map<std::string, mbgl::Value>{{"r", double(r)},
+ {"g", double(g)},
+ {"b", double(b)},
+ {"a", double(a)}};
+}
+
} // namespace mbgl
diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp
index d3364f5e53..885c8ec3d1 100644
--- a/src/mbgl/util/i18n.cpp
+++ b/src/mbgl/util/i18n.cpp
@@ -3,6 +3,7 @@
#include <algorithm>
#include <map>
+#include <mapbox/eternal.hpp>
namespace {
@@ -320,7 +321,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF)
// DEFINE_IS_IN_UNICODE_BLOCK(SupplementaryPrivateUseAreaA, 0xF0000, 0xFFFFF)
// DEFINE_IS_IN_UNICODE_BLOCK(SupplementaryPrivateUseAreaB, 0x100000, 0x10FFFF)
-const std::map<char16_t, char16_t> verticalPunctuation = {
+MAPBOX_ETERNAL_CONSTEXPR const auto verticalPunctuation = mapbox::eternal::map<char16_t, char16_t>({
{ u'!', u'︕' }, { u'#', u'#' }, { u'$', u'$' }, { u'%', u'%' }, { u'&', u'&' },
{ u'(', u'︵' }, { u')', u'︶' }, { u'*', u'*' }, { u'+', u'+' }, { u',', u'︐' },
{ u'-', u'︲' }, { u'.', u'・' }, { u'/', u'/' }, { u':', u'︓' }, { u';', u'︔' },
@@ -338,7 +339,8 @@ const std::map<char16_t, char16_t> verticalPunctuation = {
{ u'>', u'﹀' }, { u'?', u'︖' }, { u'[', u'﹇' }, { u']', u'﹈' }, { u'_', u'︳' },
{ u'{', u'︷' }, { u'|', u'―' }, { u'}', u'︸' }, { u'⦅', u'︵' }, { u'⦆', u'︶' },
{ u'。', u'︒' }, { u'「', u'﹁' }, { u'」', u'﹂' },
-};
+});
+
} // namespace
namespace mbgl {
@@ -641,6 +643,14 @@ bool isStringInSupportedScript(const std::string& input) {
return true;
}
+bool isCharInComplexShapingScript(char16_t chr) {
+ return isInArabic(chr) ||
+ isInArabicSupplement(chr) ||
+ isInArabicExtendedA(chr) ||
+ isInArabicPresentationFormsA(chr) ||
+ isInArabicPresentationFormsB(chr);
+}
+
bool isWhitespace(char16_t chr) {
return chr == u' ' || chr == u'\t' || chr == u'\n' || chr == u'\v' || chr == u'\f' || chr == u'\r';
}
diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp
index c7544f443b..7e75aa06f7 100644
--- a/src/mbgl/util/i18n.hpp
+++ b/src/mbgl/util/i18n.hpp
@@ -75,6 +75,8 @@ char16_t verticalizePunctuation(char16_t chr);
bool isStringInSupportedScript(const std::string& input);
+bool isCharInComplexShapingScript(char16_t chr);
+
bool isWhitespace(char16_t chr);
} // namespace i18n
diff --git a/src/mbgl/util/tile_coordinate.hpp b/src/mbgl/util/tile_coordinate.hpp
index b6bdc5f590..6d78804947 100644
--- a/src/mbgl/util/tile_coordinate.hpp
+++ b/src/mbgl/util/tile_coordinate.hpp
@@ -13,6 +13,7 @@ using TileCoordinatePoint = Point<double>;
// Has floating point x/y coordinates.
// Used for computing the tiles that need to be visible in the viewport.
+// In mapbox-gl-js, this is named MercatorCoordinate.
class TileCoordinate {
public:
TileCoordinatePoint p;
@@ -23,8 +24,8 @@ public:
return { Projection::project(latLng, scale) / util::tileSize, zoom };
}
- static TileCoordinate fromScreenCoordinate(const TransformState& state, double zoom, const ScreenCoordinate& screenCoordinate) {
- return fromLatLng(zoom, state.screenCoordinateToLatLng(screenCoordinate));
+ static TileCoordinate fromScreenCoordinate(const TransformState& state, uint8_t zoom, const ScreenCoordinate& screenCoordinate) {
+ return state.screenCoordinateToTileCoordinate(screenCoordinate, zoom);
}
TileCoordinate zoomTo(double zoom) const {
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index 5189b79f26..9e2451e1af 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -84,7 +84,7 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl,
const Point<double>& br,
const Point<double>& bl,
const Point<double>& c,
- int32_t z) {
+ uint8_t z) {
const int32_t tiles = 1 << z;
struct ID {
@@ -139,7 +139,7 @@ int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) {
}
}
-std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) {
+std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, uint8_t z) {
if (bounds_.isEmpty() ||
bounds_.south() > util::LATITUDE_MAX ||
bounds_.north() < -util::LATITUDE_MAX) {
@@ -159,7 +159,7 @@ std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) {
z);
}
-std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
+std::vector<UnwrappedTileID> tileCover(const TransformState& state, uint8_t z) {
assert(state.valid());
const double w = state.getSize().width;
@@ -173,7 +173,7 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
z);
}
-std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, int32_t z) {
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, uint8_t z) {
std::vector<UnwrappedTileID> result;
TileCover tc(geometry, z, true);
while (tc.hasNext()) {
@@ -213,7 +213,7 @@ uint64_t tileCount(const Geometry<double>& geometry, uint8_t z) {
return tileCount;
}
-TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) {
+TileCover::TileCover(const LatLngBounds&bounds_, uint8_t z) {
LatLngBounds bounds = LatLngBounds::hull(
{ std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() },
{ std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() });
@@ -233,7 +233,7 @@ TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) {
impl = std::make_unique<TileCover::Impl>(z, p, false);
}
-TileCover::TileCover(const Geometry<double>& geom, int32_t z, bool project/* = true*/)
+TileCover::TileCover(const Geometry<double>& geom, uint8_t z, bool project/* = true*/)
: impl( std::make_unique<TileCover::Impl>(z, geom, project)) {
}
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index c953d764d2..5ac3537bf6 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -18,9 +18,9 @@ namespace util {
// Helper class to stream tile-cover results per row
class TileCover {
public:
- TileCover(const LatLngBounds&, int32_t z);
+ TileCover(const LatLngBounds&, uint8_t z);
// When project == true, projects the geometry points to tile coordinates
- TileCover(const Geometry<double>&, int32_t z, bool project = true);
+ TileCover(const Geometry<double>&, uint8_t z, bool project = true);
~TileCover();
optional<UnwrappedTileID> next();
@@ -33,9 +33,9 @@ private:
int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize);
-std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z);
-std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z);
-std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, int32_t z);
+std::vector<UnwrappedTileID> tileCover(const TransformState&, uint8_t z);
+std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, uint8_t z);
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, uint8_t z);
// Compute only the count of tiles needed for tileCover
uint64_t tileCount(const LatLngBounds&, uint8_t z);
diff --git a/test/fixtures/local_glyphs/mixed.json b/test/fixtures/local_glyphs/mixed.json
index b982fd9641..3d943084e4 100644
--- a/test/fixtures/local_glyphs/mixed.json
+++ b/test/fixtures/local_glyphs/mixed.json
@@ -68,7 +68,7 @@
{
"type": "Feature",
"properties": {
- "name": "身什戰アあ"
+ "name": "안녕 세상 KR"
},
"geometry": {
"type": "LineString",
@@ -144,7 +144,7 @@
{
"type": "Feature",
"properties": {
- "name": "8身什戰アあ"
+ "name": "안녕 세상8"
},
"geometry": {
"type": "LineString",
diff --git a/test/fixtures/local_glyphs/no_local/expected.png b/test/fixtures/local_glyphs/no_local/expected.png
index 77552c32fa..7e2049518b 100644
--- a/test/fixtures/local_glyphs/no_local/expected.png
+++ b/test/fixtures/local_glyphs/no_local/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png b/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png
index 52ef264590..da5e6957dd 100644
--- a/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png
+++ b/test/fixtures/local_glyphs/no_local_with_content_insets/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png b/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png
index a1aa5fcb52..2501471562 100644
--- a/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png
+++ b/test/fixtures/local_glyphs/no_local_with_content_insets_and_pitch/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.png b/test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.png
new file mode 100644
index 0000000000..0d69075172
--- /dev/null
+++ b/test/fixtures/local_glyphs/noto_sans_cjk_kr_regular_qt/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/ping_fang/expected.png b/test/fixtures/local_glyphs/ping_fang/expected.png
index 44c24c276a..2ec62e1bc6 100644
--- a/test/fixtures/local_glyphs/ping_fang/expected.png
+++ b/test/fixtures/local_glyphs/ping_fang/expected.png
Binary files differ
diff --git a/test/fixtures/local_glyphs/ping_fang_qt/expected.png b/test/fixtures/local_glyphs/ping_fang_qt/expected.png
index 465bce5b77..0ede615967 100644
--- a/test/fixtures/local_glyphs/ping_fang_qt/expected.png
+++ b/test/fixtures/local_glyphs/ping_fang_qt/expected.png
Binary files differ
diff --git a/test/fixtures/resources/glyphs-12244-12543.pbf b/test/fixtures/resources/glyphs-12244-12543.pbf
new file mode 100644
index 0000000000..cbc4a52056
--- /dev/null
+++ b/test/fixtures/resources/glyphs-12244-12543.pbf
Binary files differ
diff --git a/test/geometry/dem_data.test.cpp b/test/geometry/dem_data.test.cpp
index bf362820a0..24893d16ed 100644
--- a/test/geometry/dem_data.test.cpp
+++ b/test/geometry/dem_data.test.cpp
@@ -33,14 +33,6 @@ TEST(DEMData, ConstructorTerrarium) {
EXPECT_EQ(demdata.getImage()->bytes(), size_t(18*18*4));
};
-TEST(DEMData, RoundTrip) {
- PremultipliedImage image = fakeImage({16, 16});
- DEMData demdata(image, Tileset::DEMEncoding::Mapbox);
-
- demdata.set(4, 6, 255);
- EXPECT_EQ(demdata.get(4, 6), 255);
-}
-
TEST(DEMData, InitialBackfill) {
PremultipliedImage image1 = fakeImage({4, 4});
diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp
index f97fd53919..a3dbdb8f99 100644
--- a/test/gl/bucket.test.cpp
+++ b/test/gl/bucket.test.cpp
@@ -115,17 +115,20 @@ TEST(Buckets, SymbolBucket) {
gfx::BackendScope scope { backend };
auto layout = makeMutable<style::SymbolLayoutProperties::PossiblyEvaluated>();
- bool sdfIcons = false;
bool iconsNeedLinear = false;
bool sortFeaturesByY = false;
std::string bucketLeaderID = "test";
std::vector<SymbolInstance> symbolInstances;
gl::Context context{ backend };
- SymbolBucket bucket { std::move(layout), {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances), 1.0f };
+ SymbolBucket bucket { std::move(layout), {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances), 1.0f, false, {}};
ASSERT_FALSE(bucket.hasIconData());
+ ASSERT_FALSE(bucket.hasSdfIconData());
ASSERT_FALSE(bucket.hasTextData());
- ASSERT_FALSE(bucket.hasCollisionBoxData());
+ ASSERT_FALSE(bucket.hasIconCollisionBoxData());
+ ASSERT_FALSE(bucket.hasTextCollisionBoxData());
+ ASSERT_FALSE(bucket.hasIconCollisionCircleData());
+ ASSERT_FALSE(bucket.hasTextCollisionCircleData());
ASSERT_FALSE(bucket.hasData());
ASSERT_FALSE(bucket.needsUpload());
diff --git a/test/gl/context.test.cpp b/test/gl/context.test.cpp
index 4b6bad6f65..770434c5be 100644
--- a/test/gl/context.test.cpp
+++ b/test/gl/context.test.cpp
@@ -91,7 +91,7 @@ TEST(GLContextMode, Shared) {
util::RunLoop loop;
- HeadlessFrontend frontend { 1, {}, gfx::ContextMode::Shared };
+ HeadlessFrontend frontend { 1, gfx::ContextMode::Shared };
Map map(frontend, MapObserver::nullObserver(),
MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize()),
diff --git a/test/include/mbgl/test.hpp b/test/include/mbgl/test.hpp
index ad5b868f30..db47ebb8d8 100644
--- a/test/include/mbgl/test.hpp
+++ b/test/include/mbgl/test.hpp
@@ -1,7 +1,9 @@
#pragma once
+#include <mbgl/util/util.hpp>
+
namespace mbgl {
-int runTests(int argc, char* argv[]);
+MBGL_EXPORT int runTests(int argc, char* argv[]);
} // namespace mbgl
diff --git a/test/src/mbgl/test/util.hpp b/test/include/mbgl/test/util.hpp
index 9f56841dcc..9f56841dcc 100644
--- a/test/src/mbgl/test/util.hpp
+++ b/test/include/mbgl/test/util.hpp
diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp
index 8cb781c6df..09e1b92336 100644
--- a/test/map/map.test.cpp
+++ b/test/map/map.test.cpp
@@ -762,7 +762,7 @@ TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) {
HeadlessFrontend frontend(1);
StubMapObserver observer;
- observer.didFinishRenderingFrameCallback = [&] (MapObserver::RenderMode) {
+ observer.didFinishRenderingFrameCallback = [&] (MapObserver::RenderFrameStatus) {
// 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.
@@ -877,4 +877,53 @@ TEST(Map, Issue15216) {
test.map.getStyle().addLayer(std::make_unique<RasterLayer>("RasterLayer", "ImageSource"));
// Passes, if there is no assertion hit.
test.runLoop.runOnce();
+}
+
+// https://github.com/mapbox/mapbox-gl-native/issues/15342
+// Tests the fix for constant repaint caused by `RenderSource::hasFadingTiles()` returning `true` all the time.
+TEST(Map, Issue15342) {
+ MapTest<> test { 1, MapMode::Continuous };
+
+ test.fileSource->tileResponse = [&](const Resource&) {
+ Response result;
+ result.data = std::make_shared<std::string>(util::read_file("test/fixtures/map/issue12432/0-0-0.mvt"));
+ return result;
+ };
+ test.map.jumpTo(CameraOptions().withZoom(3.0));
+ test.map.getStyle().loadJSON(R"STYLE({
+ "version": 8,
+ "sources": {
+ "mapbox": {
+ "type": "vector",
+ "tiles": ["http://example.com/{z}-{x}-{y}.vector.pbf"]
+ }
+ },
+ "layers": [{
+ "id": "water",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "water"
+ }]
+ })STYLE");
+
+ test.observer.didFinishLoadingMapCallback = [&]() {
+ test.map.getStyle().loadJSON(R"STYLE({
+ "version": 8,
+ "sources": {
+ "mapbox": {
+ "type": "vector",
+ "tiles": ["http://example.com/{z}-{x}-{y}.vector.pbf"]
+ }
+ },
+ "layers": []
+ })STYLE");
+ test.map.jumpTo(CameraOptions().withZoom(20.0));
+ test.observer.didFinishRenderingFrameCallback = [&] (MapObserver::RenderFrameStatus status) {
+ if (!status.needsRepaint) {
+ test.runLoop.stop();
+ }
+ };
+ };
+
+ test.runLoop.run();
} \ No newline at end of file
diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp
index 0b72578c35..16700d713f 100644
--- a/test/renderer/image_manager.test.cpp
+++ b/test/renderer/image_manager.test.cpp
@@ -50,6 +50,18 @@ TEST(ImageManager, AddRemove) {
EXPECT_EQ(nullptr, imageManager.getImage("four"));
}
+TEST(ImageManager, Update) {
+ FixtureLog log;
+ ImageManager imageManager;
+
+ imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2));
+ EXPECT_EQ(0, imageManager.updatedImageVersions.size());
+ imageManager.updateImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2));
+ EXPECT_EQ(1, imageManager.updatedImageVersions.size());
+ imageManager.removeImage("one");
+ EXPECT_EQ(0, imageManager.updatedImageVersions.size());
+}
+
TEST(ImageManager, RemoveReleasesBinPackRect) {
FixtureLog log;
ImageManager imageManager;
diff --git a/test/src/mbgl/test/sqlite3_test_fs.cpp b/test/src/mbgl/test/sqlite3_test_fs.cpp
index e4f958c900..068ae9e1cf 100644
--- a/test/src/mbgl/test/sqlite3_test_fs.cpp
+++ b/test/src/mbgl/test/sqlite3_test_fs.cpp
@@ -278,6 +278,7 @@ SQLite3TestFS::~SQLite3TestFS() {
sqlite3_vfs* test_fs = sqlite3_vfs_find("test_fs");
if (test_fs) {
sqlite3_vfs_unregister(test_fs);
+ sqlite3_free((void*)test_fs);
}
}
diff --git a/test/src/mbgl/test/stub_map_observer.hpp b/test/src/mbgl/test/stub_map_observer.hpp
index 00a039e732..89ee4e7953 100644
--- a/test/src/mbgl/test/stub_map_observer.hpp
+++ b/test/src/mbgl/test/stub_map_observer.hpp
@@ -32,9 +32,9 @@ public:
}
}
- void onDidFinishRenderingFrame(RenderMode mode) final {
+ void onDidFinishRenderingFrame(RenderFrameStatus status) final {
if (didFinishRenderingFrameCallback) {
- didFinishRenderingFrameCallback(mode);
+ didFinishRenderingFrameCallback(status);
}
}
@@ -42,7 +42,7 @@ public:
std::function<void()> didFinishLoadingMapCallback;
std::function<void()> didFailLoadingMapCallback;
std::function<void()> didFinishLoadingStyleCallback;
- std::function<void(RenderMode)> didFinishRenderingFrameCallback;
+ std::function<void(RenderFrameStatus)> didFinishRenderingFrameCallback;
};
diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp
index 58dd7e9e6a..24234b0624 100644
--- a/test/storage/offline_database.test.cpp
+++ b/test/storage/offline_database.test.cpp
@@ -515,7 +515,8 @@ TEST(OfflineDatabase, GetRegionDefinition) {
);
}
-TEST(OfflineDatabase, TEST_REQUIRES_WRITE(MaximumAmbientCacheSize)) {
+// Disabled due to flakiness: https://github.com/mapbox/mapbox-gl-native/issues/14966
+TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DISABLED_MaximumAmbientCacheSize)) {
FixtureLog log;
deleteDatabaseFiles();
@@ -1092,8 +1093,8 @@ TEST(OfflineDatabase, HasRegionResource) {
auto region = db.createRegion(definition, OfflineRegionMetadata());
ASSERT_TRUE(region);
- EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/1"))));
- EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20"))));
+ EXPECT_FALSE(bool(db.hasRegionResource(Resource::style("http://example.com/1"))));
+ EXPECT_FALSE(bool(db.hasRegionResource(Resource::style("http://example.com/20"))));
Response response;
response.data = randomString(1024);
@@ -1102,9 +1103,9 @@ TEST(OfflineDatabase, HasRegionResource) {
db.putRegionResource(region->getID(), Resource::style("http://example.com/"s + util::toString(i)), response);
}
- EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/1"))));
- EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20"))));
- EXPECT_EQ(1024, *(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20"))));
+ EXPECT_TRUE(bool(db.hasRegionResource(Resource::style("http://example.com/1"))));
+ EXPECT_TRUE(bool(db.hasRegionResource(Resource::style("http://example.com/20"))));
+ EXPECT_EQ(1024, *(db.hasRegionResource(Resource::style("http://example.com/20"))));
EXPECT_EQ(0u, log.uncheckedCount());
}
@@ -1130,16 +1131,16 @@ TEST(OfflineDatabase, HasRegionResourceTile) {
response.data = std::make_shared<std::string>("first");
- EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), resource)));
+ EXPECT_FALSE(bool(db.hasRegionResource(resource)));
db.putRegionResource(region->getID(), resource, response);
- EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), resource)));
- EXPECT_EQ(5, *(db.hasRegionResource(region->getID(), resource)));
+ EXPECT_TRUE(bool(db.hasRegionResource(resource)));
+ EXPECT_EQ(5, *(db.hasRegionResource(resource)));
auto anotherRegion = db.createRegion(definition, OfflineRegionMetadata());
ASSERT_TRUE(anotherRegion);
EXPECT_LT(region->getID(), anotherRegion->getID());
- EXPECT_TRUE(bool(db.hasRegionResource(anotherRegion->getID(), resource)));
- EXPECT_EQ(5, *(db.hasRegionResource(anotherRegion->getID(), resource)));
+ EXPECT_TRUE(bool(db.hasRegionResource(resource)));
+ EXPECT_EQ(5, *(db.hasRegionResource(resource)));
EXPECT_EQ(0u, log.uncheckedCount());
@@ -1487,12 +1488,12 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DisallowedIO)) {
EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update region metadata: authorization denied")));
EXPECT_EQ(0u, log.uncheckedCount());
- EXPECT_EQ(nullopt, db.getRegionResource(region->getID(), fixture::resource));
+ EXPECT_EQ(nullopt, db.getRegionResource(fixture::resource));
EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update timestamp: authorization denied")));
EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't read region resource: authorization denied")));
EXPECT_EQ(0u, log.uncheckedCount());
- EXPECT_EQ(nullopt, db.hasRegionResource(region->getID(), fixture::resource));
+ EXPECT_EQ(nullopt, db.hasRegionResource(fixture::resource));
EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't query region resource: authorization denied")));
EXPECT_EQ(0u, log.uncheckedCount());
@@ -1565,7 +1566,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(MergeDatabaseWithSingleRegion_Update))
EXPECT_EQ(1u, status->completedTileCount);
//Verify the modified timestamp matches the tile in the sideloaded db.
- auto updatedTile = db.getRegionResource(regionId,
+ auto updatedTile = db.getRegionResource(
Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.webp",
1, 0, 0, 1, Tileset::Scheme::XYZ));
EXPECT_EQ(Timestamp{ Seconds(1520409600) }, *(updatedTile->first.modified));
@@ -1585,8 +1586,7 @@ TEST(OfflineDatabase, MergeDatabaseWithSingleRegion_NoUpdate) {
EXPECT_EQ(1u, result->size());
EXPECT_EQ(1u, db.listRegions()->size());
- auto regionId = result->front().getID();
- auto updatedTile = db.getRegionResource(regionId,
+ auto updatedTile = db.getRegionResource(
Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.webp",
1, 0, 0, 1, Tileset::Scheme::XYZ));
@@ -1600,14 +1600,13 @@ TEST(OfflineDatabase, MergeDatabaseWithSingleRegion_AmbientTiles) {
OfflineDatabase db(":memory:");
auto result = db.mergeDatabase(filename_sideload);
- auto regionId = result->front().getID();
- EXPECT_TRUE(bool(db.hasRegionResource(regionId, Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 0, 1, Tileset::Scheme::XYZ))));
+ EXPECT_TRUE(bool(db.hasRegionResource(Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 0, 1, Tileset::Scheme::XYZ))));
//Ambient resources should not be copied
- EXPECT_FALSE(bool(db.hasRegionResource(regionId, Resource::style("mapbox://styles/mapbox/streets-v9"))));
- EXPECT_FALSE(bool(db.hasRegionResource(regionId, Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 1, 2, Tileset::Scheme::XYZ))));
- EXPECT_FALSE(bool(db.hasRegionResource(regionId, Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 1, 1, 2, Tileset::Scheme::XYZ))));
+ EXPECT_FALSE(bool(db.hasRegionResource(Resource::style("mapbox://styles/mapbox/streets-v9"))));
+ EXPECT_FALSE(bool(db.hasRegionResource(Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 0, 1, 2, Tileset::Scheme::XYZ))));
+ EXPECT_FALSE(bool(db.hasRegionResource(Resource::tile("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", 1, 1, 1, 2, Tileset::Scheme::XYZ))));
}
TEST(OfflineDatabase, MergeDatabaseWithMultipleRegions_New) {
diff --git a/test/storage/offline_download.test.cpp b/test/storage/offline_download.test.cpp
index 9cdafaaf1e..ebe3f82ee9 100644
--- a/test/storage/offline_download.test.cpp
+++ b/test/storage/offline_download.test.cpp
@@ -1,8 +1,12 @@
+#include <mbgl/test/map_adapter.hpp>
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/test/fake_file_source.hpp>
+#include <mbgl/test/stub_map_observer.hpp>
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/test/sqlite3_test_fs.hpp>
+#include <mbgl/gfx/headless_frontend.hpp>
+#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/offline.hpp>
#include <mbgl/storage/offline_database.hpp>
#include <mbgl/storage/offline_download.hpp>
@@ -14,7 +18,6 @@
#include <mbgl/storage/sqlite3.hpp>
#include <gtest/gtest.h>
-#include <iostream>
using namespace mbgl;
using namespace std::literals::string_literals;
@@ -71,6 +74,10 @@ public:
return db.createRegion(definition, metadata);
}
+ auto invalidateRegion(int64_t region) {
+ return db.invalidateRegion(region);
+ }
+
Response response(const std::string& path) {
Response result;
result.data = std::make_shared<std::string>(util::read_file("test/fixtures/offline_download/"s + path));
@@ -150,6 +157,7 @@ TEST(OfflineDownload, InlineSource) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
+ EXPECT_EQ(1u, status.completedTileCount);
EXPECT_EQ(2u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
@@ -253,6 +261,7 @@ TEST(OfflineDownload, Activate) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
+ EXPECT_EQ(status.completedTileCount, status.requiredTileCount);
EXPECT_EQ(264u, status.completedResourceCount); // 256 glyphs, 2 sprite images, 2 sprite jsons, 1 tile, 1 style, source, image
EXPECT_EQ(test.size, status.completedResourceSize);
@@ -260,6 +269,7 @@ TEST(OfflineDownload, Activate) {
OfflineRegionStatus computedStatus = download.getStatus();
EXPECT_EQ(OfflineRegionDownloadState::Inactive, computedStatus.downloadState);
EXPECT_EQ(status.requiredResourceCount, computedStatus.requiredResourceCount);
+ EXPECT_EQ(status.requiredTileCount, computedStatus.requiredTileCount);
EXPECT_EQ(status.completedResourceCount, computedStatus.completedResourceCount);
EXPECT_EQ(status.completedResourceSize, computedStatus.completedResourceSize);
EXPECT_EQ(status.completedTileCount, computedStatus.completedTileCount);
@@ -330,6 +340,7 @@ TEST(OfflineDownload, ExcludeIdeographs) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
+ EXPECT_EQ(status.completedTileCount, status.requiredTileCount);
EXPECT_EQ(138u, status.completedResourceCount); // 130 glyphs, 2 sprite images, 2 sprite jsons, 1 tile, 1 style, source, image
EXPECT_EQ(test.size, status.completedResourceSize);
@@ -337,6 +348,7 @@ TEST(OfflineDownload, ExcludeIdeographs) {
OfflineRegionStatus computedStatus = download.getStatus();
EXPECT_EQ(OfflineRegionDownloadState::Inactive, computedStatus.downloadState);
EXPECT_EQ(status.requiredResourceCount, computedStatus.requiredResourceCount);
+ EXPECT_EQ(status.requiredTileCount, computedStatus.requiredTileCount);
EXPECT_EQ(status.completedResourceCount, computedStatus.completedResourceCount);
EXPECT_EQ(status.completedResourceSize, computedStatus.completedResourceSize);
EXPECT_EQ(status.completedTileCount, computedStatus.completedTileCount);
@@ -391,6 +403,8 @@ TEST(OfflineDownload, GetStatusNoResources) {
EXPECT_EQ(0u, status.completedResourceCount);
EXPECT_EQ(0u, status.completedResourceSize);
EXPECT_EQ(1u, status.requiredResourceCount);
+ EXPECT_EQ(0u, status.completedTileCount);
+ EXPECT_EQ(0u, status.requiredTileCount);
EXPECT_FALSE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
@@ -414,6 +428,8 @@ TEST(OfflineDownload, GetStatusStyleComplete) {
EXPECT_EQ(1u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_EQ(263u, status.requiredResourceCount);
+ EXPECT_EQ(0u, status.completedTileCount);
+ EXPECT_EQ(0u, status.requiredTileCount);
EXPECT_FALSE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
@@ -441,6 +457,8 @@ TEST(OfflineDownload, GetStatusStyleAndSourceComplete) {
EXPECT_EQ(2u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_EQ(264u, status.requiredResourceCount);
+ EXPECT_EQ(0u, status.completedTileCount);
+ EXPECT_EQ(1u, status.requiredTileCount);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
@@ -857,4 +875,75 @@ TEST(OfflineDownload, DiskFull) {
test.loop.run();
EXPECT_EQ(0u, log.uncheckedCount());
}
+
+// Test verifies that resource requests for invalidated region don't
+// have Resource::Usage::Offline tag set.
+TEST(OfflineDownload, ResourceOfflineUsageUnset) {
+ deleteDatabaseFiles();
+ test::SQLite3TestFS fs;
+
+ OfflineTest test{ filename_test_fs };
+ auto region = test.createRegion();
+ ASSERT_TRUE(region);
+
+ OfflineDownload download(
+ region->getID(),
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/inline_source.style.json",
+ LatLngBounds::world(), 0.0, 0.0, 1.0, false),
+ test.db, test.fileSource);
+
+ test.fileSource.styleResponse = [&] (const Resource& resource) {
+ EXPECT_TRUE(resource.priority == Resource::Priority::Low);
+ EXPECT_TRUE(resource.usage == Resource::Usage::Offline);
+ return test.response("inline_source.style.json");
+ };
+
+ test.fileSource.tileResponse = [&] (const Resource& resource) {
+ EXPECT_TRUE(resource.priority == Resource::Priority::Low);
+ EXPECT_TRUE(resource.usage == Resource::Usage::Offline);
+ return test.response("0-0-0.vector.pbf");
+ };
+
+ auto observer = std::make_unique<MockObserver>();
+ observer->statusChangedFn = [&] (OfflineRegionStatus status) {
+ if (status.complete()) {
+ // Once download completes, invalidate region and try rendering map.
+ // Resource requests must not have Offline usage tag.
+ ASSERT_FALSE(test.invalidateRegion(region->getID()));
+ test.loop.stop();
+ }
+ };
+
+ download.setObserver(std::move(observer));
+ download.setState(OfflineRegionDownloadState::Active);
+ test.loop.run();
+
+ std::shared_ptr<StubFileSource> stubfileSource = std::make_shared<StubFileSource>();
+ stubfileSource->styleResponse = [&] (const Resource& resource) {
+ EXPECT_TRUE(resource.usage != Resource::Usage::Offline);
+ return test.response("inline_source.style.json");
+ };
+
+ stubfileSource->tileResponse = [&] (const Resource& resource) {
+ EXPECT_TRUE(resource.usage != Resource::Usage::Offline);
+ return test.response("0-0-0.vector.pbf");
+ };
+
+ StubMapObserver mapObserver;
+ mapObserver.didFinishRenderingFrameCallback = [&] (MapObserver::RenderFrameStatus status) {
+ if (status.mode == MapObserver::RenderMode::Full) {
+ test.loop.stop();
+ }
+ };
+
+ HeadlessFrontend frontend { 1 };
+ MapAdapter map { frontend, mapObserver, stubfileSource,
+ MapOptions()
+ .withMapMode(MapMode::Continuous)
+ .withSize(frontend.getSize())};
+
+ map.getStyle().loadURL("http://127.0.0.1:3000/inline_source.style.json");
+ map.jumpTo(CameraOptions().withCenter(LatLng{0.0, 0.0}).withZoom(0));
+ test.loop.run();
+}
#endif // __QT__
diff --git a/test/storage/sync_file_source.test.cpp b/test/storage/sync_file_source.test.cpp
new file mode 100644
index 0000000000..4bd964199d
--- /dev/null
+++ b/test/storage/sync_file_source.test.cpp
@@ -0,0 +1,50 @@
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/gfx/headless_frontend.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/map/map_impl.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/test/map_adapter.hpp>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+using namespace mbgl;
+
+class SyncFileSource : public FileSource {
+public:
+ std::unique_ptr<AsyncRequest> request(const Resource& resource, FileSource::Callback callback) {
+ Response response;
+ auto it = assets.find(resource.url);
+ if (it == assets.end()) {
+ response.error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, std::string{ "Not Found: " } + resource.url);
+ } else {
+ response.data = it->second;
+ }
+ callback(response);
+ return nullptr;
+ }
+
+ void add(std::string const& key, std::string const& data) {
+ assets.emplace(key, std::make_shared<std::string>(data));
+ };
+
+private:
+ std::unordered_map<std::string, std::shared_ptr<std::string>> assets;
+};
+
+TEST(SyncFileSource, LoadSyncRender) {
+ util::RunLoop loop;
+ auto fs = std::make_shared<SyncFileSource>();
+ fs->add("mapbox://mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7",
+ util::read_file("test/fixtures/resources/source_vector.json"));
+ fs->add("mapbox://sprites/mapbox/streets-v9.png",
+ util::read_file("test/fixtures/resources/sprite.png"));
+ fs->add("mapbox://sprites/mapbox/streets-v9.json",
+ util::read_file("test/fixtures/resources/sprite.json"));
+ HeadlessFrontend frontend{ { 512, 512 }, 1.0 };
+ MapAdapter map{ frontend, MapObserver::nullObserver(), fs, MapOptions() };
+ map.getStyle().loadJSON(util::read_file("test/fixtures/resources/style_vector.json"));
+}
diff --git a/test/style/conversion/geojson_options.test.cpp b/test/style/conversion/geojson_options.test.cpp
index 181189775b..aa84686dce 100644
--- a/test/style/conversion/geojson_options.test.cpp
+++ b/test/style/conversion/geojson_options.test.cpp
@@ -38,6 +38,7 @@ TEST(GeoJSONOptions, RetainsDefaults) {
ASSERT_EQ(converted.cluster, defaults.cluster);
ASSERT_EQ(converted.clusterRadius, defaults.clusterRadius);
ASSERT_EQ(converted.clusterMaxZoom, defaults.clusterMaxZoom);
+ ASSERT_TRUE(converted.clusterProperties.empty());
}
TEST(GeoJSONOptions, FullConversion) {
@@ -49,7 +50,12 @@ TEST(GeoJSONOptions, FullConversion) {
"cluster": true,
"clusterRadius": 4,
"clusterMaxZoom": 5,
- "lineMetrics": true
+ "lineMetrics": true,
+ "clusterProperties": {
+ "max": ["max", ["get", "scalerank"]],
+ "sum": [["+", ["accumulated"], ["get", "sum"]], ["get", "scalerank"]],
+ "has_island": ["any", ["==", ["get", "featureclass"], "island"]]
+ }
})JSON", error);
// GeoJSON-VT
@@ -63,4 +69,8 @@ TEST(GeoJSONOptions, FullConversion) {
ASSERT_EQ(converted.cluster, true);
ASSERT_EQ(converted.clusterRadius, 4);
ASSERT_EQ(converted.clusterMaxZoom, 5);
+ ASSERT_EQ(converted.clusterProperties.size(), 3);
+ ASSERT_EQ(converted.clusterProperties.count("max"), 1);
+ ASSERT_EQ(converted.clusterProperties.count("sum"), 1);
+ ASSERT_EQ(converted.clusterProperties.count("has_island"), 1);
}
diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp
index 092c476277..e49667f319 100644
--- a/test/style/conversion/light.test.cpp
+++ b/test/style/conversion/light.test.cpp
@@ -5,8 +5,10 @@
#include <mbgl/style/conversion/light.hpp>
#include <mbgl/style/conversion_impl.hpp>
#include <mbgl/style/position.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/rapidjson.hpp>
#include <array>
@@ -24,6 +26,27 @@ TEST(StyleConversion, Light) {
{
auto light = parseLight("{}");
ASSERT_TRUE((bool) light);
+
+ const mbgl::JSValue colorValue("blue");
+ light->setProperty("color", &colorValue);
+
+ ASSERT_FALSE(light->getColor().isUndefined());
+ ASSERT_TRUE(light->getColor().isConstant());
+ ASSERT_EQ(light->getColor().asConstant(), mbgl::Color::blue());
+
+ const mbgl::JSValue intensityValue(0.5);
+ light->setProperty("intensity", &intensityValue);
+ ASSERT_FALSE(light->getIntensity().isUndefined());
+ ASSERT_TRUE(light->getIntensity().isConstant());
+ ASSERT_EQ(light->getIntensity().asConstant(), 0.5);
+
+ mbgl::JSValue::AllocatorType allocator;
+ const mbgl::JSValue positionValue(std::move(mbgl::JSValue(rapidjson::kArrayType).PushBack(1.f, allocator).PushBack(2.f, allocator).PushBack(3.f, allocator).Move()));
+ light->setProperty("position", &positionValue);
+ ASSERT_FALSE(light->getPosition().isUndefined());
+ ASSERT_TRUE(light->getPosition().isConstant());
+ std::array<float, 3> expected{{ 1.f, 2.f, 3.f }};
+ ASSERT_EQ(light->getPosition().asConstant(), mbgl::style::Position({ expected }));
}
{
diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp
index 8e2acfcd32..dd986c98f5 100644
--- a/test/style/expression/expression.test.cpp
+++ b/test/style/expression/expression.test.cpp
@@ -36,8 +36,7 @@ TEST(Expression, IsExpression) {
// TODO: "feature-state": https://github.com/mapbox/mapbox-gl-native/issues/12613
// TODO: "interpolate-hcl": https://github.com/mapbox/mapbox-gl-native/issues/8720
// TODO: "interpolate-lab": https://github.com/mapbox/mapbox-gl-native/issues/8720
- // TODO: "accumulated": https://github.com/mapbox/mapbox-gl-native/issues/14043
- if (name == "feature-state" || name == "interpolate-hcl" || name == "interpolate-lab" || name == "accumulated") {
+ if (name == "feature-state" || name == "interpolate-hcl" || name == "interpolate-lab") {
if (expression::isExpression(conversion::Convertible(expression))) {
ASSERT_TRUE(false) << "Expression name" << name << "is implemented - please update Expression.IsExpression test.";
}
diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp
index 40219108bb..f3f5b52141 100644
--- a/test/style/filter.test.cpp
+++ b/test/style/filter.test.cpp
@@ -255,3 +255,10 @@ TEST(Filter, Internal) {
TEST(Filter, Short) {
filter(R"(["==", ["id"], "foo"])");
}
+
+TEST(Filter, LegacyExpressionInvalidType) {
+ const JSValue value("string");
+ conversion::Error error;
+ optional<Filter> result = conversion::convert<Filter>(conversion::Convertible(&value), error);
+ EXPECT_FALSE(result);
+}
diff --git a/test/test-files.json b/test/test-files.json
index e46f833269..a98e896e7e 100644
--- a/test/test-files.json
+++ b/test/test-files.json
@@ -46,6 +46,7 @@
"test/storage/online_file_source.test.cpp",
"test/storage/resource.test.cpp",
"test/storage/sqlite.test.cpp",
+ "test/storage/sync_file_source.test.cpp",
"test/style/conversion/conversion_impl.test.cpp",
"test/style/conversion/function.test.cpp",
"test/style/conversion/geojson_options.test.cpp",
@@ -92,7 +93,6 @@
"test/util/merge_lines.test.cpp",
"test/util/number_conversions.test.cpp",
"test/util/offscreen_texture.test.cpp",
- "test/util/peer.test.cpp",
"test/util/position.test.cpp",
"test/util/projection.test.cpp",
"test/util/run_loop.test.cpp",
@@ -107,7 +107,8 @@
"test/util/url.test.cpp"
],
"public_headers": {
- "mbgl/test.hpp": "test/include/mbgl/test.hpp"
+ "mbgl/test.hpp": "test/include/mbgl/test.hpp",
+ "mbgl/test/util.hpp": "test/include/mbgl/test/util.hpp"
},
"private_headers": {
"mbgl/test/fake_file_source.hpp": "test/src/mbgl/test/fake_file_source.hpp",
@@ -122,7 +123,6 @@
"mbgl/test/stub_map_observer.hpp": "test/src/mbgl/test/stub_map_observer.hpp",
"mbgl/test/stub_render_source_observer.hpp": "test/src/mbgl/test/stub_render_source_observer.hpp",
"mbgl/test/stub_style_observer.hpp": "test/src/mbgl/test/stub_style_observer.hpp",
- "mbgl/test/stub_tile_observer.hpp": "test/src/mbgl/test/stub_tile_observer.hpp",
- "mbgl/test/util.hpp": "test/src/mbgl/test/util.hpp"
+ "mbgl/test/stub_tile_observer.hpp": "test/src/mbgl/test/stub_tile_observer.hpp"
}
}
diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp
index 7cef287b00..4ff84063f9 100644
--- a/test/text/cross_tile_symbol_index.test.cpp
+++ b/test/text/cross_tile_symbol_index.test.cpp
@@ -13,12 +13,13 @@ SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) {
Anchor anchor(x, y, 0, 0);
std::array<float, 2> textOffset{{0.0f, 0.0f}};
std::array<float, 2> iconOffset{{0.0f, 0.0f}};
+ std::array<float, 2> variableTextOffset{{0.0f, 0.0f}};
style::SymbolPlacementType placementType = style::SymbolPlacementType::Point;
auto sharedData = std::make_shared<SymbolInstanceSharedData>(std::move(line),
- shaping, nullopt, layout_, 0.0f, placementType,
- textOffset, positions);
- return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0, 0, 0.0f);
+ shaping, nullopt, nullopt, layout_, placementType,
+ textOffset, positions, false);
+ return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, variableTextOffset, false);
}
@@ -30,7 +31,6 @@ TEST(CrossTileSymbolLayerIndex, addBucket) {
Immutable<style::SymbolLayoutProperties::PossiblyEvaluated> layout =
makeMutable<style::SymbolLayoutProperties::PossiblyEvaluated>();
- bool sdfIcons = false;
bool iconsNeedLinear = false;
bool sortFeaturesByY = false;
std::string bucketLeaderID = "test";
@@ -39,7 +39,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) {
std::vector<SymbolInstance> mainInstances;
mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit"));
mainInstances.push_back(makeSymbolInstance(2000, 2000, u"Toronto"));
- SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances), 1.0f };
+ SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances), 1.0f, false, {} };
mainBucket.bucketInstanceId = ++maxBucketInstanceId;
index.addBucket(mainID, mainBucket, maxCrossTileID);
@@ -54,7 +54,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) {
childInstances.push_back(makeSymbolInstance(2000, 2000, u"Windsor"));
childInstances.push_back(makeSymbolInstance(3000, 3000, u"Toronto"));
childInstances.push_back(makeSymbolInstance(4001, 4001, u"Toronto"));
- SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances), 1.0f };
+ SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances), 1.0f, false, {} };
childBucket.bucketInstanceId = ++maxBucketInstanceId;
index.addBucket(childID, childBucket, maxCrossTileID);
@@ -70,7 +70,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) {
OverscaledTileID parentID(5, 0, 5, 4, 4);
std::vector<SymbolInstance> parentInstances;
parentInstances.push_back(makeSymbolInstance(500, 500, u"Detroit"));
- SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(parentInstances), 1.0f };
+ SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(parentInstances), 1.0f, false, {} };
parentBucket.bucketInstanceId = ++maxBucketInstanceId;
index.addBucket(parentID, parentBucket, maxCrossTileID);
@@ -86,7 +86,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) {
std::vector<SymbolInstance> grandchildInstances;
grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Detroit"));
grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Windsor"));
- SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(grandchildInstances), 1.0f };
+ SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(grandchildInstances), 1.0f, false, {} };
grandchildBucket.bucketInstanceId = ++maxBucketInstanceId;
index.addBucket(grandchildID, grandchildBucket, maxCrossTileID);
@@ -105,7 +105,6 @@ TEST(CrossTileSymbolLayerIndex, resetIDs) {
Immutable<style::SymbolLayoutProperties::PossiblyEvaluated> layout =
makeMutable<style::SymbolLayoutProperties::PossiblyEvaluated>();
- bool sdfIcons = false;
bool iconsNeedLinear = false;
bool sortFeaturesByY = false;
std::string bucketLeaderID = "test";
@@ -113,13 +112,13 @@ TEST(CrossTileSymbolLayerIndex, resetIDs) {
OverscaledTileID mainID(6, 0, 6, 8, 8);
std::vector<SymbolInstance> mainInstances;
mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit"));
- SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances), 1.0f };
+ SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances), 1.0f, false, {} };
mainBucket.bucketInstanceId = ++maxBucketInstanceId;
OverscaledTileID childID(7, 0, 7, 16, 16);
std::vector<SymbolInstance> childInstances;
childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit"));
- SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances), 1.0f };
+ SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances), 1.0f, false, {} };
childBucket.bucketInstanceId = ++maxBucketInstanceId;
// assigns a new id
@@ -146,7 +145,6 @@ TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) {
Immutable<style::SymbolLayoutProperties::PossiblyEvaluated> layout =
makeMutable<style::SymbolLayoutProperties::PossiblyEvaluated>();
- bool sdfIcons = false;
bool iconsNeedLinear = false;
bool sortFeaturesByY = false;
std::string bucketLeaderID = "test";
@@ -155,7 +153,7 @@ TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) {
std::vector<SymbolInstance> mainInstances;
mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A
mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B
- SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances), 1.0f };
+ SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances), 1.0f, false, {} };
mainBucket.bucketInstanceId = ++maxBucketInstanceId;
OverscaledTileID childID(7, 0, 7, 16, 16);
@@ -163,7 +161,7 @@ TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) {
childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // A'
childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // B'
childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // C'
- SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances), 1.0f };
+ SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances), 1.0f, false, {} };
childBucket.bucketInstanceId = ++maxBucketInstanceId;
// assigns new ids
@@ -185,7 +183,6 @@ TEST(CrossTileSymbolLayerIndex, bucketReplacement) {
Immutable<style::SymbolLayoutProperties::PossiblyEvaluated> layout =
makeMutable<style::SymbolLayoutProperties::PossiblyEvaluated>();
- bool sdfIcons = false;
bool iconsNeedLinear = false;
bool sortFeaturesByY = false;
std::string bucketLeaderID = "test";
@@ -194,14 +191,14 @@ TEST(CrossTileSymbolLayerIndex, bucketReplacement) {
std::vector<SymbolInstance> firstInstances;
firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A
firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B
- SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(firstInstances), 1.0f };
+ SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(firstInstances), 1.0f, false, {} };
firstBucket.bucketInstanceId = ++maxBucketInstanceId;
std::vector<SymbolInstance> secondInstances;
secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A'
secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B'
secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // C'
- SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(secondInstances), 1.0f };
+ SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(secondInstances), 1.0f, false, {} };
secondBucket.bucketInstanceId = ++maxBucketInstanceId;
// assigns new ids
diff --git a/test/text/glyph_manager.test.cpp b/test/text/glyph_manager.test.cpp
index 1193780631..1e9aef38ed 100644
--- a/test/text/glyph_manager.test.cpp
+++ b/test/text/glyph_manager.test.cpp
@@ -20,7 +20,7 @@ static constexpr const size_t stubBitmapLength = 900;
class StubLocalGlyphRasterizer : public LocalGlyphRasterizer {
public:
bool canRasterizeGlyph(const FontStack&, GlyphID glyphID) override {
- return util::i18n::allowsIdeographicBreaking(glyphID);
+ return util::i18n::allowsFixedWidthGlyphGeneration(glyphID);
}
Glyph rasterizeGlyph(const FontStack&, GlyphID glyphID) override {
@@ -253,6 +253,57 @@ TEST(GlyphManager, LoadLocalCJKGlyph) {
});
}
+TEST(GlyphManager, LoadLocalCJKGlyphAfterLoadingRangeFromURL) {
+ GlyphManagerTest test;
+ int firstGlyphResponse = false;
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ firstGlyphResponse = true;
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs-12244-12543.pbf"));
+ return response;
+
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap glyphs) {
+ const auto& testPositions = glyphs.at(FontStackHasher()({{"Test Stack"}}));
+
+ if (firstGlyphResponse == true) {
+ firstGlyphResponse = false;
+ ASSERT_EQ(testPositions.size(), 1u);
+ ASSERT_EQ(testPositions.count(u'々'), 1u);
+
+ //Katakana letter te, should be locally rasterized
+ // instead of using the glyph recieved from the range
+ // for the ideagraphic mark
+ test.glyphManager.getGlyphs(test.requestor,
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'テ'}} // 0x30c6
+ },
+ test.fileSource);
+ } else {
+ ASSERT_EQ(testPositions.size(), 1u);
+ ASSERT_EQ(testPositions.count(u'テ'), 1u);
+
+ Immutable<Glyph> glyph = *testPositions.at(u'テ');
+ EXPECT_EQ(glyph->id, u'テ');
+ EXPECT_EQ(glyph->metrics.width, 24ul);
+ EXPECT_EQ(glyph->metrics.height, 24ul);
+ EXPECT_EQ(glyph->metrics.left, 0);
+ EXPECT_EQ(glyph->metrics.top, -8);
+ EXPECT_EQ(glyph->metrics.advance, 24ul);
+ EXPECT_EQ(glyph->bitmap.size, Size(30, 30));
+
+ test.end();
+ }
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs-12244-12543.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'々'}} //0x3005
+ });
+}
TEST(GlyphManager, LoadingInvalid) {
GlyphManagerTest test;
diff --git a/test/text/local_glyph_rasterizer.test.cpp b/test/text/local_glyph_rasterizer.test.cpp
index 86dc87b6c7..2722ee5849 100644
--- a/test/text/local_glyph_rasterizer.test.cpp
+++ b/test/text/local_glyph_rasterizer.test.cpp
@@ -33,7 +33,7 @@ namespace {
class LocalGlyphRasterizerTest {
public:
LocalGlyphRasterizerTest(const optional<std::string> fontFamily)
- : frontend(1, optional<std::string>(), gfx::ContextMode::Unique, fontFamily)
+ : frontend(1, gfx::ContextMode::Unique, fontFamily)
{
}
@@ -43,7 +43,7 @@ public:
MapAdapter map { frontend, MapObserver::nullObserver(), fileSource,
MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize())};
- void checkRendering(const char * name, double imageMatchPixelsThreshold = 0.05, double pixelMatchThreshold = 0.1) {
+ void checkRendering(const char * name, double imageMatchPixelsThreshold = 0.015, double pixelMatchThreshold = 0.1) {
test::checkImage(std::string("test/fixtures/local_glyphs/") + name,
frontend.render(map), imageMatchPixelsThreshold, pixelMatchThreshold);
}
@@ -64,7 +64,7 @@ TEST(LocalGlyphRasterizer, PingFang) {
return response;
};
test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json"));
-#if defined(__APPLE__)
+#if defined(__APPLE__) && !defined(__QT__)
test.checkRendering("ping_fang");
#elif defined(__QT__)
test.checkRendering("ping_fang_qt");
@@ -73,6 +73,22 @@ TEST(LocalGlyphRasterizer, PingFang) {
#endif // defined(__APPLE__)
+#if defined(__linux__) && defined(__QT__)
+TEST(LocalGlyphRasterizer, NotoSansCJK) {
+ LocalGlyphRasterizerTest test(std::string("Noto Sans CJK KR Regular"));
+
+ test.fileSource->glyphsResponse = [&] (const Resource& resource) {
+ EXPECT_EQ(Resource::Kind::Glyphs, resource.kind);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/local_glyphs/mixed.json"));
+ test.checkRendering("noto_sans_cjk_kr_regular_qt");
+}
+#endif // defined(__linux__) && defined(__QT__)
+
TEST(LocalGlyphRasterizer, NoLocal) {
// Expectation: without any local fonts set, and without any CJK glyphs provided,
// the output should just contain basic latin characters.
diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp
index c032d58b88..7aaeb4870d 100644
--- a/test/text/quads.test.cpp
+++ b/test/text/quads.test.cpp
@@ -20,10 +20,9 @@ TEST(getIconQuads, normal) {
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
- Shaping shapedText;
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 16.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -14);
EXPECT_EQ(quad.tl.y, -10);
@@ -42,8 +41,6 @@ TEST(getIconQuads, style) {
style::Image::Impl("test", PremultipliedImage({1,1}), 1.0)
};
- auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
-
GeometryCoordinates line;
Shaping shapedText;
shapedText.top = -10.0f;
@@ -54,9 +51,10 @@ TEST(getIconQuads, style) {
// none
{
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
SymbolLayoutProperties::Evaluated layout;
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -19.5);
EXPECT_EQ(quad.tl.y, -19.5);
@@ -73,16 +71,18 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 24.0f);
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 24.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
- EXPECT_EQ(quad.tl.x, -60);
+ EXPECT_EQ(quad.tl.x, -61);
EXPECT_EQ(quad.tl.y, 0);
- EXPECT_EQ(quad.tr.x, 20);
+ EXPECT_EQ(quad.tr.x, 21);
EXPECT_EQ(quad.tr.y, 0);
- EXPECT_EQ(quad.bl.x, -60);
+ EXPECT_EQ(quad.bl.x, -61);
EXPECT_EQ(quad.bl.y, 20);
- EXPECT_EQ(quad.br.x, 20);
+ EXPECT_EQ(quad.br.x, 21);
EXPECT_EQ(quad.br.y, 20);
}
@@ -91,16 +91,18 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
- EXPECT_EQ(quad.tl.x, -30);
+ EXPECT_EQ(quad.tl.x, -31);
EXPECT_EQ(quad.tl.y, -5);
- EXPECT_EQ(quad.tr.x, 10);
+ EXPECT_EQ(quad.tr.x, 11);
EXPECT_EQ(quad.tr.y, -5);
- EXPECT_EQ(quad.bl.x, -30);
+ EXPECT_EQ(quad.bl.x, -31);
EXPECT_EQ(quad.bl.y, 15);
- EXPECT_EQ(quad.br.x, 10);
+ EXPECT_EQ(quad.br.x, 11);
EXPECT_EQ(quad.br.y, 15);
}
@@ -113,16 +115,18 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 10.0f;
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
- EXPECT_EQ(quad.tl.x, -40);
+ EXPECT_EQ(quad.tl.x, -41);
EXPECT_EQ(quad.tl.y, -10);
- EXPECT_EQ(quad.tr.x, 20);
+ EXPECT_EQ(quad.tr.x, 21);
EXPECT_EQ(quad.tr.y, -10);
- EXPECT_EQ(quad.bl.x, -40);
+ EXPECT_EQ(quad.bl.x, -41);
EXPECT_EQ(quad.bl.y, 20);
- EXPECT_EQ(quad.br.x, 20);
+ EXPECT_EQ(quad.br.x, 21);
EXPECT_EQ(quad.br.y, 20);
}
@@ -131,17 +135,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 24.0f);
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 24.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -30);
- EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tl.y, -11);
EXPECT_EQ(quad.tr.x, -10);
- EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.tr.y, -11);
EXPECT_EQ(quad.bl.x, -30);
- EXPECT_EQ(quad.bl.y, 30);
+ EXPECT_EQ(quad.bl.y, 31);
EXPECT_EQ(quad.br.x, -10);
- EXPECT_EQ(quad.br.y, 30);
+ EXPECT_EQ(quad.br.y, 31);
}
// height x textSize
@@ -149,17 +155,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -20);
- EXPECT_EQ(quad.tl.y, -5);
+ EXPECT_EQ(quad.tl.y, -6);
EXPECT_EQ(quad.tr.x, 0);
- EXPECT_EQ(quad.tr.y, -5);
+ EXPECT_EQ(quad.tr.y, -6);
EXPECT_EQ(quad.bl.x, -20);
- EXPECT_EQ(quad.bl.y, 15);
+ EXPECT_EQ(quad.bl.y, 16);
EXPECT_EQ(quad.br.x, 0);
- EXPECT_EQ(quad.br.y, 15);
+ EXPECT_EQ(quad.br.y, 16);
}
// height x textSize + padding
@@ -171,17 +179,19 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 10.0f;
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
- getIconQuad(shapedIcon, layout, 12.0f, shapedText);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -30);
- EXPECT_EQ(quad.tl.y, -10);
+ EXPECT_EQ(quad.tl.y, -11);
EXPECT_EQ(quad.tr.x, 10);
- EXPECT_EQ(quad.tr.y, -10);
+ EXPECT_EQ(quad.tr.y, -11);
EXPECT_EQ(quad.bl.x, -30);
- EXPECT_EQ(quad.bl.y, 20);
+ EXPECT_EQ(quad.bl.y, 21);
EXPECT_EQ(quad.br.x, 10);
- EXPECT_EQ(quad.br.y, 20);
+ EXPECT_EQ(quad.br.y, 21);
}
// both
@@ -189,17 +199,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 24.0f);
SymbolQuad quad =
- 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);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
+
+ EXPECT_EQ(quad.tl.x, -61);
+ EXPECT_EQ(quad.tl.y, -11);
+ EXPECT_EQ(quad.tr.x, 21);
+ EXPECT_EQ(quad.tr.y, -11);
+ EXPECT_EQ(quad.bl.x, -61);
+ EXPECT_EQ(quad.bl.y, 31);
+ EXPECT_EQ(quad.br.x, 21);
+ EXPECT_EQ(quad.br.y, 31);
}
// both x textSize
@@ -207,17 +219,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
- 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);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
+
+ EXPECT_EQ(quad.tl.x, -31);
+ EXPECT_EQ(quad.tl.y, -6);
+ EXPECT_EQ(quad.tr.x, 11);
+ EXPECT_EQ(quad.tr.y, -6);
+ EXPECT_EQ(quad.bl.x, -31);
+ EXPECT_EQ(quad.bl.y, 16);
+ EXPECT_EQ(quad.br.x, 11);
+ EXPECT_EQ(quad.br.y, 16);
}
// both x textSize + padding
@@ -229,17 +243,19 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 10.0f;
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
- 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);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
+
+ EXPECT_EQ(quad.tl.x, -41);
+ EXPECT_EQ(quad.tl.y, -11);
+ EXPECT_EQ(quad.tr.x, 21);
+ EXPECT_EQ(quad.tr.y, -11);
+ EXPECT_EQ(quad.bl.x, -41);
+ EXPECT_EQ(quad.bl.y, 21);
+ EXPECT_EQ(quad.br.x, 21);
+ EXPECT_EQ(quad.br.y, 21);
}
// both x textSize + padding t/r/b/l
@@ -251,17 +267,19 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 5.0f;
layout.get<IconTextFitPadding>()[2] = 10.0f;
layout.get<IconTextFitPadding>()[3] = 15.0f;
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
+ shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
- 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);
+ getIconQuad(shapedIcon, WritingModeType::Horizontal);
+
+ EXPECT_EQ(quad.tl.x, -46);
+ EXPECT_EQ(quad.tl.y, -6);
+ EXPECT_EQ(quad.tr.x, 16);
+ EXPECT_EQ(quad.tr.y, -6);
+ EXPECT_EQ(quad.bl.x, -46);
+ EXPECT_EQ(quad.bl.y, 26);
+ EXPECT_EQ(quad.br.x, 16);
+ EXPECT_EQ(quad.br.y, 26);
}
}
diff --git a/test/text/shaping.test.cpp b/test/text/shaping.test.cpp
index 547c85e5ba..b22cd7da36 100644
--- a/test/text/shaping.test.cpp
+++ b/test/text/shaping.test.cpp
@@ -33,10 +33,11 @@ TEST(Shaping, ZWSP) {
style::SymbolAnchorType::Center,
style::TextJustifyType::Center,
0, // spacing
- {0.0f, 0.0f}, // translate
+ {{0.0f, 0.0f}}, // translate
WritingModeType::Horizontal,
bidi,
- glyphs);
+ glyphs,
+ /*allowVerticalPlacement*/ false);
};
// 3 lines
diff --git a/test/util/peer.test.cpp b/test/util/peer.test.cpp
deleted file mode 100644
index a808dd27d1..0000000000
--- a/test/util/peer.test.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-#include <mbgl/test/util.hpp>
-
-#include <mbgl/util/peer.hpp>
-
-using namespace mbgl::util;
-
-class TestType {
-public:
- TestType() {
- str[0] = 'a';
- }
-
- //Detect moves
- TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) {
- str[0] = t.str[0]+1;
- }
-
- TestType(const TestType&) = delete;
- TestType& operator=(const TestType&) = delete;
-
- int i1 = 0;
- int i2 = 1;
- char str[256];
-};
-
-bool IsStackAllocated (const peer& a, const void* obj1) {
- auto a_ptr = (uintptr_t)(&a);
- auto obj = (uintptr_t)(obj1);
- return (obj >= a_ptr && obj < a_ptr + sizeof(peer));
-};
-
-TEST(Peer, Empty) {
- EXPECT_FALSE(peer().has_value());
-}
-
-TEST(Peer, BasicTypes) {
- peer i = 3;
- EXPECT_TRUE(i.has_value());
- EXPECT_TRUE(i.get<decltype(3)>() == 3);
-
- auto iValue = i.get<decltype(3)>();
- EXPECT_TRUE(iValue == 3);
-
- EXPECT_TRUE(peer(4).has_value());
- EXPECT_TRUE(peer(4).get<decltype(4)>() == 4);
-
- peer f = 6.2f;
- EXPECT_TRUE(f.has_value());
- EXPECT_TRUE(f.get<decltype(6.2f)>() == 6.2f);
-
- const float fValue = f.get<decltype(6.2f)>();
- EXPECT_TRUE(fValue == 6.2f);
-
- EXPECT_TRUE(peer(1.0f).has_value());
- EXPECT_TRUE(peer(1.0f).get<decltype(1.0f)>() == 1.0f);
-
- peer c = 'z';
- EXPECT_TRUE(c.has_value());
- EXPECT_TRUE(c.get<decltype('z')>() == 'z');
-
- EXPECT_TRUE(peer('z').has_value());
- EXPECT_TRUE(peer('z').get<decltype('z')>() == 'z');
-}
-
-TEST(Peer, BasicTypes_Move) {
- peer i = 3;
- EXPECT_TRUE(i.has_value());
-
- peer f = 6.2f;
- EXPECT_TRUE(f.has_value());
-
- f = std::move(i);
- EXPECT_FALSE(i.has_value());
-
- EXPECT_TRUE(f.has_value());
- EXPECT_TRUE(f.get<decltype(3)>() == 3);
-}
-
-TEST(Peer, SmallType) {
- struct T {
- T(int32_t* p_) : p(p_) {
- (*p)++;
- }
-
- T(T&& t) noexcept : p(t.p) {
- (*p)++;
- }
-
- ~T() {
- (*p)--;
- }
-
- T(const T&) = delete;
- T& operator=(const T&) = delete;
-
- int32_t* p;
- };
-
- int32_t p = 0;
-
- {
- peer u1 = peer(T(&p));
- EXPECT_EQ(p, 1);
-
- auto u2(std::move(u1));
- EXPECT_EQ(p, 1);
- }
-
- EXPECT_EQ(p, 0);
-}
-
-TEST(Peer, LargeType) {
- TestType t1;
- peer u1 = peer(std::move(t1));
- EXPECT_TRUE(u1.has_value());
-
- //TestType should be moved into owning peer
- EXPECT_EQ(u1.get<TestType>().i1, 1);
-
- auto u2(std::move(u1));
- EXPECT_FALSE(u1.has_value());
-
- //TestType should not be moved when owning peer is moved;
- EXPECT_EQ(u2.get<TestType>().i1, 1);
-
- // TestType should not be moved out of owning peer
- auto& t2 = u2.get<TestType>();
- EXPECT_TRUE(u2.has_value());
- EXPECT_EQ(t2.i1, 1);
-}
-
-TEST(Peer, Pointer) {
- auto t1 = new TestType();
-
- auto u1 = peer(std::move(t1));
- EXPECT_TRUE(u1.has_value());
-
- //Only the pointer should be moved
- TestType * t2 = u1.get<TestType *>();
- EXPECT_EQ(t2->i1, 0);
-
- peer u2(4);
- std::swap(u2, u1);
-
- EXPECT_TRUE(u1.has_value());
-
- EXPECT_TRUE(u2.has_value());
-
- t2 = u2.get<TestType *>();
- EXPECT_EQ(t2->i1, 0);
- delete t2;
-}
-
-
-TEST(Peer, UniquePtr) {
- auto t1 = std::make_unique<TestType>();
- auto u1 = peer(std::move(t1));
-
- EXPECT_EQ(t1.get(), nullptr);
- EXPECT_TRUE(u1.has_value());
-
- u1 = peer();
- EXPECT_FALSE(u1.has_value());
-
- peer u2;
- auto* t3 = new TestType();
- u2 = std::unique_ptr<TestType>(t3);
- EXPECT_TRUE(u2.has_value());
-}
-
-TEST(Peer, SharedPtr) {
-
- std::shared_ptr<int> shared(new int(3));
- std::weak_ptr<int> weak = shared;
- peer u1 = 0;
-
- EXPECT_EQ(weak.use_count(), 1);
- peer u2 = shared;
- EXPECT_EQ(weak.use_count(), 2);
-
- u1 = std::move(u2);
- EXPECT_EQ(weak.use_count(), 2);
- std::swap(u2, u1);
- EXPECT_EQ(weak.use_count(), 2);
- u2 = 0;
- EXPECT_EQ(weak.use_count(), 1);
- shared = nullptr;
- EXPECT_EQ(weak.use_count(), 0);
-}
diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp
index e35e6e2e99..af9f0c4884 100644
--- a/test/util/tile_cover.test.cpp
+++ b/test/util/tile_cover.test.cpp
@@ -43,6 +43,20 @@ TEST(TileCover, Pitch) {
util::tileCover(transform.getState(), 2));
}
+TEST(TileCover, PitchIssue15442) {
+ Transform transform;
+ transform.resize({ 412, 691 });
+
+ transform.jumpTo(CameraOptions().withCenter(LatLng { 59.116898740996106, 91.565660781803615, })
+ .withZoom(2.0551126748417214).withBearing(0.74963938256567264 * util::RAD2DEG)
+ .withPitch(1.0471975511965976 * util::RAD2DEG));
+
+ EXPECT_EQ((std::vector<UnwrappedTileID>{
+ { 2, 3, 1 }, { 2, 2, 1 }, { 2, 3, 0 }, { 2, 2, 0 }, { 1, { 2, 0, 0 } }, { 1, { 2, 1, 0 } }
+ }),
+ util::tileCover(transform.getState(), 2));
+}
+
TEST(TileCover, PitchOverAllowedByContentInsets) {
Transform transform;
transform.resize({ 512, 512 });
diff --git a/vendor/args b/vendor/args
deleted file mode 160000
-Subproject f68b7e186cd2a020cbddfe3194c1d8ddfeeb101
diff --git a/vendor/args-files.json b/vendor/args-files.json
deleted file mode 100644
index c3c9bb64c6..0000000000
--- a/vendor/args-files.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "args.hxx": "vendor/args/args.hxx"
- },
- "private_headers": {}
-}
diff --git a/vendor/args.cmake b/vendor/args.cmake
deleted file mode 100644
index b5839943c2..0000000000
--- a/vendor/args.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-add_library(args INTERFACE)
-
-target_include_directories(args SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/args
-)
diff --git a/vendor/benchmark.cmake b/vendor/benchmark.cmake
index 516e3cd333..44c0672971 100644
--- a/vendor/benchmark.cmake
+++ b/vendor/benchmark.cmake
@@ -1,25 +1,31 @@
-add_library(benchmark STATIC
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/commandlineflags.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/console_reporter.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/complexity.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/csv_reporter.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/colorprint.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/sleep.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/benchmark.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/counter.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/benchmark_register.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/statistics.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/json_reporter.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/reporter.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/string_util.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/sysinfo.cc
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/src/timers.cc
+if(TARGET mbgl-vendor-benchmark)
+ return()
+endif()
+
+add_library(mbgl-vendor-benchmark STATIC EXCLUDE_FROM_ALL
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/commandlineflags.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/console_reporter.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/complexity.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/csv_reporter.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/colorprint.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/sleep.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/benchmark.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/counter.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/benchmark_register.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/statistics.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/json_reporter.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/reporter.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/string_util.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/sysinfo.cc
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/src/timers.cc
)
-target_compile_definitions(benchmark PRIVATE
+target_compile_definitions(mbgl-vendor-benchmark PRIVATE
HAVE_STEADY_CLOCK
)
-target_include_directories(benchmark SYSTEM PUBLIC
- ${CMAKE_SOURCE_DIR}/vendor/benchmark/include
+target_include_directories(mbgl-vendor-benchmark SYSTEM PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}/benchmark/include
)
+
+set_property(TARGET mbgl-vendor-benchmark PROPERTY FOLDER Core)
diff --git a/vendor/boost.cmake b/vendor/boost.cmake
index 7c30a8d90e..33b203368e 100644
--- a/vendor/boost.cmake
+++ b/vendor/boost.cmake
@@ -1,5 +1,9 @@
-add_library(boost INTERFACE)
+if(TARGET mbgl-vendor-boost)
+ return()
+endif()
-target_include_directories(boost SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/boost/include
+add_library(mbgl-vendor-boost INTERFACE)
+
+target_include_directories(mbgl-vendor-boost SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/boost/include
)
diff --git a/vendor/cheap-ruler-cpp.cmake b/vendor/cheap-ruler-cpp.cmake
index 8d98cde63c..741a7b2ec2 100644
--- a/vendor/cheap-ruler-cpp.cmake
+++ b/vendor/cheap-ruler-cpp.cmake
@@ -1,6 +1,10 @@
-add_library(cheap-ruler-cpp INTERFACE)
+if(TARGET mbgl-vendor-cheap-ruler-cpp)
+ return()
+endif()
-target_include_directories(cheap-ruler-cpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/cheap-ruler-cpp/include
+add_library(mbgl-vendor-cheap-ruler-cpp INTERFACE)
+
+target_include_directories(mbgl-vendor-cheap-ruler-cpp SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/cheap-ruler-cpp/include
)
diff --git a/vendor/earcut.hpp.cmake b/vendor/earcut.hpp.cmake
index d7992cda50..e5459f16ce 100644
--- a/vendor/earcut.hpp.cmake
+++ b/vendor/earcut.hpp.cmake
@@ -1,5 +1,9 @@
-add_library(earcut.hpp INTERFACE)
+if(TARGET mbgl-vendor-earcut.hpp)
+ return()
+endif()
-target_include_directories(earcut.hpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/earcut.hpp/include
+add_library(mbgl-vendor-earcut.hpp INTERFACE)
+
+target_include_directories(mbgl-vendor-earcut.hpp SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/earcut.hpp/include
)
diff --git a/vendor/eternal.cmake b/vendor/eternal.cmake
index 1239a8a43d..5a429db4fa 100644
--- a/vendor/eternal.cmake
+++ b/vendor/eternal.cmake
@@ -1,5 +1,9 @@
-add_library(eternal INTERFACE)
+if(TARGET mbgl-vendor-eternal)
+ return()
+endif()
-target_include_directories(eternal SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/eternal/include
+add_library(mbgl-vendor-eternal INTERFACE)
+
+target_include_directories(mbgl-vendor-eternal SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/eternal/include
)
diff --git a/vendor/expected.cmake b/vendor/expected.cmake
index ebe63e1a10..f87f057386 100644
--- a/vendor/expected.cmake
+++ b/vendor/expected.cmake
@@ -1,5 +1,9 @@
-add_library(expected INTERFACE)
+if(TARGET mbgl-vendor-expected)
+ return()
+endif()
-target_include_directories(expected SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/expected/include
+add_library(mbgl-vendor-expected INTERFACE)
+
+target_include_directories(mbgl-vendor-expected SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/expected/include
)
diff --git a/vendor/filesystem b/vendor/filesystem
deleted file mode 160000
-Subproject 091c08663ac3e38aea1ccaeae235340f5154f5a
diff --git a/vendor/filesystem-files.json b/vendor/filesystem-files.json
deleted file mode 100644
index d65e9c828e..0000000000
--- a/vendor/filesystem-files.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "ghc/filesystem.hpp": "vendor/filesystem/include/ghc/filesystem.hpp",
- "ghc/fs_fwd.hpp": "vendor/filesystem/include/ghc/fs_fwd.hpp",
- "ghc/fs_impl.hpp": "vendor/filesystem/include/ghc/fs_impl.hpp",
- "ghc/fs_std.hpp": "vendor/filesystem/include/ghc/fs_std.hpp",
- "ghc/fs_std_fwd.hpp": "vendor/filesystem/include/ghc/fs_std_fwd.hpp",
- "ghc/fs_std_impl.hpp": "vendor/filesystem/include/ghc/fs_std_impl.hpp"
- },
- "private_headers": {}
-}
diff --git a/vendor/filesystem.cmake b/vendor/filesystem.cmake
deleted file mode 100644
index fe2701890d..0000000000
--- a/vendor/filesystem.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-add_library(filesystem INTERFACE)
-
-target_include_directories(filesystem SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/filesystem/include
-)
diff --git a/vendor/geojson-vt-cpp.cmake b/vendor/geojson-vt-cpp.cmake
index c8d955f64b..f2646336b3 100644
--- a/vendor/geojson-vt-cpp.cmake
+++ b/vendor/geojson-vt-cpp.cmake
@@ -1,5 +1,9 @@
-add_library(geojson-vt-cpp INTERFACE)
+if(TARGET mbgl-vendor-geojson-vt-cpp)
+ return()
+endif()
-target_include_directories(geojson-vt-cpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/geojson-vt-cpp/include
+add_library(mbgl-vendor-geojson-vt-cpp INTERFACE)
+
+target_include_directories(mbgl-vendor-geojson-vt-cpp SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/geojson-vt-cpp/include
)
diff --git a/vendor/geojson.hpp b/vendor/geojson.hpp
deleted file mode 160000
-Subproject 97f81eadb66f985af4ce59c003bce8718654198
diff --git a/vendor/geojson.hpp-files.json b/vendor/geojson.hpp-files.json
deleted file mode 100644
index 609351bc0a..0000000000
--- a/vendor/geojson.hpp-files.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "mapbox/geojson.hpp": "vendor/geojson.hpp/include/mapbox/geojson.hpp",
- "mapbox/geojson/rapidjson.hpp": "vendor/geojson.hpp/include/mapbox/geojson/rapidjson.hpp",
- "mapbox/geojson_impl.hpp": "vendor/geojson.hpp/include/mapbox/geojson_impl.hpp"
- },
- "private_headers": {}
-}
diff --git a/vendor/geojson.hpp.cmake b/vendor/geojson.hpp.cmake
deleted file mode 100644
index 261650388f..0000000000
--- a/vendor/geojson.hpp.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-add_library(geojson.hpp INTERFACE)
-
-target_include_directories(geojson.hpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/geojson.hpp/include
-)
diff --git a/vendor/googletest.cmake b/vendor/googletest.cmake
index edf8ebd82e..4ae0afddb5 100644
--- a/vendor/googletest.cmake
+++ b/vendor/googletest.cmake
@@ -1,16 +1,22 @@
-add_library(googletest STATIC
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googletest/src/gtest-all.cc
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googlemock/src/gmock-all.cc
+if(TARGET mbgl-vendor-googletest)
+ return()
+endif()
+
+add_library(mbgl-vendor-googletest STATIC EXCLUDE_FROM_ALL
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/src/gmock-all.cc
)
-target_include_directories(googletest PRIVATE
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googletest
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googletest/include
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googlemock
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googlemock/include
+target_include_directories(mbgl-vendor-googletest PRIVATE
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/include
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/include
)
-target_include_directories(googletest SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googletest/include
- ${CMAKE_SOURCE_DIR}/vendor/googletest/googlemock/include
+target_include_directories(mbgl-vendor-googletest SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/include
+ ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/include
)
+
+set_property(TARGET mbgl-vendor-googletest PROPERTY FOLDER Core)
diff --git a/vendor/icu.cmake b/vendor/icu.cmake
index 419b78333a..c3a05fe44b 100644
--- a/vendor/icu.cmake
+++ b/vendor/icu.cmake
@@ -1,22 +1,26 @@
-add_library(icu STATIC
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/cmemory.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/cstring.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/ubidi.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/ubidi_props.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/ubidiln.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/ubidiwrt.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/uchar.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/udataswp.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/uinvchar.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/umath.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/ushape.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/ustring.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/utf_impl.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/utrie2.cpp
- ${CMAKE_SOURCE_DIR}/vendor/icu/src/utypes.cpp
+if(TARGET mbgl-vendor-icu)
+ return()
+endif()
+
+add_library(mbgl-vendor-icu STATIC
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/cmemory.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/cstring.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/ubidi.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/ubidi_props.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/ubidiln.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/ubidiwrt.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/uchar.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/udataswp.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/uinvchar.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/umath.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/ushape.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/ustring.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/utf_impl.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/utrie2.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/icu/src/utypes.cpp
)
-target_compile_definitions(icu PRIVATE
+target_compile_definitions(mbgl-vendor-icu PRIVATE
UCONFIG_NO_BREAK_ITERATION=1
UCONFIG_NO_LEGACY_CONVERSION=1
U_CHARSET_IS_UTF8=1
@@ -26,11 +30,13 @@ target_compile_definitions(icu PRIVATE
_REENTRANT
)
-target_compile_options(icu PRIVATE
+target_compile_options(mbgl-vendor-icu PRIVATE
-Wno-error
-Wno-shorten-64-to-32
)
-target_include_directories(icu SYSTEM PUBLIC
- ${CMAKE_SOURCE_DIR}/vendor/icu/include
+target_include_directories(mbgl-vendor-icu SYSTEM PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}/icu/include
)
+
+set_property(TARGET mbgl-vendor-icu PROPERTY FOLDER Core)
diff --git a/vendor/jni.hpp b/vendor/jni.hpp
deleted file mode 160000
-Subproject 8f55acd9017452f45a88ab3fb3aef89de995b72
diff --git a/vendor/jni.hpp-files.json b/vendor/jni.hpp-files.json
deleted file mode 100644
index f4d93abc74..0000000000
--- a/vendor/jni.hpp-files.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "jni/advanced_ownership.hpp": "vendor/jni.hpp/include/jni/advanced_ownership.hpp",
- "jni/array.hpp": "vendor/jni.hpp/include/jni/array.hpp",
- "jni/arraylike.hpp": "vendor/jni.hpp/include/jni/arraylike.hpp",
- "jni/boxing.hpp": "vendor/jni.hpp/include/jni/boxing.hpp",
- "jni/class.hpp": "vendor/jni.hpp/include/jni/class.hpp",
- "jni/constructor.hpp": "vendor/jni.hpp/include/jni/constructor.hpp",
- "jni/errors.hpp": "vendor/jni.hpp/include/jni/errors.hpp",
- "jni/field.hpp": "vendor/jni.hpp/include/jni/field.hpp",
- "jni/functions.hpp": "vendor/jni.hpp/include/jni/functions.hpp",
- "jni/jni.hpp": "vendor/jni.hpp/include/jni/jni.hpp",
- "jni/make.hpp": "vendor/jni.hpp/include/jni/make.hpp",
- "jni/method.hpp": "vendor/jni.hpp/include/jni/method.hpp",
- "jni/native_method.hpp": "vendor/jni.hpp/include/jni/native_method.hpp",
- "jni/npe.hpp": "vendor/jni.hpp/include/jni/npe.hpp",
- "jni/object.hpp": "vendor/jni.hpp/include/jni/object.hpp",
- "jni/ownership.hpp": "vendor/jni.hpp/include/jni/ownership.hpp",
- "jni/static_field.hpp": "vendor/jni.hpp/include/jni/static_field.hpp",
- "jni/static_method.hpp": "vendor/jni.hpp/include/jni/static_method.hpp",
- "jni/string.hpp": "vendor/jni.hpp/include/jni/string.hpp",
- "jni/tagging.hpp": "vendor/jni.hpp/include/jni/tagging.hpp",
- "jni/traits.hpp": "vendor/jni.hpp/include/jni/traits.hpp",
- "jni/type_signature.hpp": "vendor/jni.hpp/include/jni/type_signature.hpp",
- "jni/typed_methods.hpp": "vendor/jni.hpp/include/jni/typed_methods.hpp",
- "jni/types.hpp": "vendor/jni.hpp/include/jni/types.hpp",
- "jni/unique.hpp": "vendor/jni.hpp/include/jni/unique.hpp",
- "jni/weak_reference.hpp": "vendor/jni.hpp/include/jni/weak_reference.hpp",
- "jni/wrapping.hpp": "vendor/jni.hpp/include/jni/wrapping.hpp"
- },
- "private_headers": {}
-}
diff --git a/vendor/jni.hpp.cmake b/vendor/jni.hpp.cmake
deleted file mode 100644
index bf3c129858..0000000000
--- a/vendor/jni.hpp.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-add_library(jni.hpp INTERFACE)
-
-target_include_directories(jni.hpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/jni.hpp/include
-)
diff --git a/vendor/kdbush.hpp b/vendor/kdbush.hpp
deleted file mode 160000
-Subproject bbbbf6030f46d28add4d8e1b1436b89af3ffb92
diff --git a/vendor/kdbush.hpp-files.json b/vendor/kdbush.hpp-files.json
deleted file mode 100644
index 94cbfb453d..0000000000
--- a/vendor/kdbush.hpp-files.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "kdbush.hpp": "vendor/kdbush.hpp/include/kdbush.hpp"
- },
- "private_headers": {}
-}
diff --git a/vendor/kdbush.hpp.cmake b/vendor/kdbush.hpp.cmake
deleted file mode 100644
index 450530b7c6..0000000000
--- a/vendor/kdbush.hpp.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-add_library(kdbush.hpp INTERFACE)
-
-target_include_directories(kdbush.hpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/kdbush.hpp/include
-)
diff --git a/vendor/mapbox-base b/vendor/mapbox-base
-Subproject 13fa9eced82977791d814a7d549b2b6929b82b3
+Subproject 8ca46e8f8ebd212d7e55bd2b076b7ee42eaca5b
diff --git a/vendor/mapbox-base-files.json b/vendor/mapbox-base-files.json
index 2e952b9661..7c638d2b80 100644
--- a/vendor/mapbox-base-files.json
+++ b/vendor/mapbox-base-files.json
@@ -2,28 +2,105 @@
"//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
"sources": [],
"public_headers": {
- "mapbox/feature.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/feature.hpp",
- "mapbox/geometry.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry.hpp",
- "mapbox/geometry/box.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/box.hpp",
- "mapbox/geometry/empty.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/empty.hpp",
- "mapbox/geometry/envelope.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/envelope.hpp",
- "mapbox/geometry/for_each_point.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/for_each_point.hpp",
- "mapbox/geometry/geometry.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/geometry.hpp",
- "mapbox/geometry/line_string.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/line_string.hpp",
- "mapbox/geometry/multi_line_string.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/multi_line_string.hpp",
- "mapbox/geometry/multi_point.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/multi_point.hpp",
- "mapbox/geometry/multi_polygon.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/multi_polygon.hpp",
- "mapbox/geometry/point.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/point.hpp",
- "mapbox/geometry/point_arithmetic.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/point_arithmetic.hpp",
- "mapbox/geometry/polygon.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry/polygon.hpp",
- "mapbox/geometry_io.hpp": "vendor/mapbox-base/libs/geometry.hpp/include/mapbox/geometry_io.hpp",
- "optional.hpp": "vendor/mapbox-base/libs/optional/optional.hpp",
- "mapbox/optional.hpp": "vendor/mapbox-base/libs/variant/include/mapbox/optional.hpp",
- "mapbox/recursive_wrapper.hpp": "vendor/mapbox-base/libs/variant/include/mapbox/recursive_wrapper.hpp",
- "mapbox/variant.hpp": "vendor/mapbox-base/libs/variant/include/mapbox/variant.hpp",
- "mapbox/variant_cast.hpp": "vendor/mapbox-base/libs/variant/include/mapbox/variant_cast.hpp",
- "mapbox/variant_io.hpp": "vendor/mapbox-base/libs/variant/include/mapbox/variant_io.hpp",
- "mapbox/variant_visitor.hpp": "vendor/mapbox-base/libs/variant/include/mapbox/variant_visitor.hpp"
+ "ghc/filesystem.hpp": "vendor/mapbox-base/extras/filesystem/include/ghc/filesystem.hpp",
+ "ghc/fs_fwd.hpp": "vendor/mapbox-base/extras/filesystem/include/ghc/fs_fwd.hpp",
+ "ghc/fs_impl.hpp": "vendor/mapbox-base/extras/filesystem/include/ghc/fs_impl.hpp",
+ "ghc/fs_std.hpp": "vendor/mapbox-base/extras/filesystem/include/ghc/fs_std.hpp",
+ "ghc/fs_std_fwd.hpp": "vendor/mapbox-base/extras/filesystem/include/ghc/fs_std_fwd.hpp",
+ "ghc/fs_std_impl.hpp": "vendor/mapbox-base/extras/filesystem/include/ghc/fs_std_impl.hpp",
+ "kdbush.hpp": "vendor/mapbox-base/extras/kdbush.hpp/include/kdbush.hpp",
+ "rapidjson/allocators.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/allocators.h",
+ "rapidjson/cursorstreamwrapper.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/cursorstreamwrapper.h",
+ "rapidjson/document.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/document.h",
+ "rapidjson/encodedstream.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/encodedstream.h",
+ "rapidjson/encodings.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/encodings.h",
+ "rapidjson/error/en.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/error/en.h",
+ "rapidjson/error/error.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/error/error.h",
+ "rapidjson/filereadstream.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/filereadstream.h",
+ "rapidjson/filewritestream.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/filewritestream.h",
+ "rapidjson/fwd.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/fwd.h",
+ "rapidjson/internal/biginteger.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/biginteger.h",
+ "rapidjson/internal/diyfp.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/diyfp.h",
+ "rapidjson/internal/dtoa.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/dtoa.h",
+ "rapidjson/internal/ieee754.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/ieee754.h",
+ "rapidjson/internal/itoa.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/itoa.h",
+ "rapidjson/internal/meta.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/meta.h",
+ "rapidjson/internal/pow10.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/pow10.h",
+ "rapidjson/internal/regex.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/regex.h",
+ "rapidjson/internal/stack.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/stack.h",
+ "rapidjson/internal/strfunc.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/strfunc.h",
+ "rapidjson/internal/strtod.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/strtod.h",
+ "rapidjson/internal/swap.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/internal/swap.h",
+ "rapidjson/istreamwrapper.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/istreamwrapper.h",
+ "rapidjson/memorybuffer.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/memorybuffer.h",
+ "rapidjson/memorystream.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/memorystream.h",
+ "rapidjson/msinttypes/inttypes.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/msinttypes/inttypes.h",
+ "rapidjson/msinttypes/stdint.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/msinttypes/stdint.h",
+ "rapidjson/ostreamwrapper.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/ostreamwrapper.h",
+ "rapidjson/pointer.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/pointer.h",
+ "rapidjson/prettywriter.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/prettywriter.h",
+ "rapidjson/rapidjson.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/rapidjson.h",
+ "rapidjson/reader.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/reader.h",
+ "rapidjson/schema.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/schema.h",
+ "rapidjson/stream.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/stream.h",
+ "rapidjson/stringbuffer.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/stringbuffer.h",
+ "rapidjson/writer.h": "vendor/mapbox-base/extras/rapidjson/include/rapidjson/writer.h",
+ "mapbox/geojson.hpp": "vendor/mapbox-base/mapbox/geojson.hpp/include/mapbox/geojson.hpp",
+ "mapbox/geojson/rapidjson.hpp": "vendor/mapbox-base/mapbox/geojson.hpp/include/mapbox/geojson/rapidjson.hpp",
+ "mapbox/geojson_impl.hpp": "vendor/mapbox-base/mapbox/geojson.hpp/include/mapbox/geojson_impl.hpp",
+ "mapbox/feature.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/feature.hpp",
+ "mapbox/geometry.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry.hpp",
+ "mapbox/geometry/box.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/box.hpp",
+ "mapbox/geometry/empty.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/empty.hpp",
+ "mapbox/geometry/envelope.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/envelope.hpp",
+ "mapbox/geometry/for_each_point.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/for_each_point.hpp",
+ "mapbox/geometry/geometry.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/geometry.hpp",
+ "mapbox/geometry/line_string.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/line_string.hpp",
+ "mapbox/geometry/multi_line_string.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/multi_line_string.hpp",
+ "mapbox/geometry/multi_point.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/multi_point.hpp",
+ "mapbox/geometry/multi_polygon.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/multi_polygon.hpp",
+ "mapbox/geometry/point.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/point.hpp",
+ "mapbox/geometry/point_arithmetic.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/point_arithmetic.hpp",
+ "mapbox/geometry/polygon.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry/polygon.hpp",
+ "mapbox/geometry_io.hpp": "vendor/mapbox-base/mapbox/geometry.hpp/include/mapbox/geometry_io.hpp",
+ "jni/advanced_ownership.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/advanced_ownership.hpp",
+ "jni/array.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/array.hpp",
+ "jni/arraylike.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/arraylike.hpp",
+ "jni/boxing.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/boxing.hpp",
+ "jni/class.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/class.hpp",
+ "jni/constructor.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/constructor.hpp",
+ "jni/errors.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/errors.hpp",
+ "jni/field.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/field.hpp",
+ "jni/functions.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/functions.hpp",
+ "jni/jni.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/jni.hpp",
+ "jni/make.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/make.hpp",
+ "jni/method.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/method.hpp",
+ "jni/native_method.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/native_method.hpp",
+ "jni/npe.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/npe.hpp",
+ "jni/object.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/object.hpp",
+ "jni/ownership.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/ownership.hpp",
+ "jni/static_field.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/static_field.hpp",
+ "jni/static_method.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/static_method.hpp",
+ "jni/string.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/string.hpp",
+ "jni/tagging.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/tagging.hpp",
+ "jni/traits.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/traits.hpp",
+ "jni/type_signature.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/type_signature.hpp",
+ "jni/typed_methods.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/typed_methods.hpp",
+ "jni/types.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/types.hpp",
+ "jni/unique.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/unique.hpp",
+ "jni/weak_reference.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/weak_reference.hpp",
+ "jni/wrapping.hpp": "vendor/mapbox-base/mapbox/jni.hpp/include/jni/wrapping.hpp",
+ "optional.hpp": "vendor/mapbox-base/mapbox/optional/optional.hpp",
+ "mapbox/pixelmatch.hpp": "vendor/mapbox-base/mapbox/pixelmatch-cpp/include/mapbox/pixelmatch.hpp",
+ "supercluster.hpp": "vendor/mapbox-base/mapbox/supercluster.hpp/include/supercluster.hpp",
+ "mapbox/type_wrapper.hpp": "vendor/mapbox-base/mapbox/typewrapper/include/mapbox/type_wrapper.hpp",
+ "mapbox/optional.hpp": "vendor/mapbox-base/mapbox/variant/include/mapbox/optional.hpp",
+ "mapbox/recursive_wrapper.hpp": "vendor/mapbox-base/mapbox/variant/include/mapbox/recursive_wrapper.hpp",
+ "mapbox/variant.hpp": "vendor/mapbox-base/mapbox/variant/include/mapbox/variant.hpp",
+ "mapbox/variant_cast.hpp": "vendor/mapbox-base/mapbox/variant/include/mapbox/variant_cast.hpp",
+ "mapbox/variant_io.hpp": "vendor/mapbox-base/mapbox/variant/include/mapbox/variant_io.hpp",
+ "mapbox/variant_visitor.hpp": "vendor/mapbox-base/mapbox/variant/include/mapbox/variant_visitor.hpp",
+ "mapbox/weak.hpp": "vendor/mapbox-base/mapbox/weak/include/mapbox/weak.hpp"
},
"private_headers": {}
}
diff --git a/vendor/nunicode.cmake b/vendor/nunicode.cmake
index d318b8a265..0a310a7bea 100644
--- a/vendor/nunicode.cmake
+++ b/vendor/nunicode.cmake
@@ -1,21 +1,25 @@
-add_library(nunicode STATIC
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/src/libnu/ducet.c
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/src/libnu/strcoll.c
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/src/libnu/strings.c
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/src/libnu/tolower.c
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/src/libnu/tounaccent.c
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/src/libnu/toupper.c
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/src/libnu/utf8.c
+if(TARGET mbgl-vendor-nunicode)
+ return()
+endif()
+
+add_library(mbgl-vendor-nunicode STATIC
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/src/libnu/ducet.c
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/src/libnu/strcoll.c
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/src/libnu/strings.c
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/src/libnu/tolower.c
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/src/libnu/tounaccent.c
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/src/libnu/toupper.c
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/src/libnu/utf8.c
)
-target_compile_definitions(nunicode PRIVATE
+target_compile_definitions(mbgl-vendor-nunicode PRIVATE
NU_BUILD_STATIC
)
-target_compile_options(nunicode PRIVATE
+target_compile_options(mbgl-vendor-nunicode PRIVATE
-Wno-error
)
-target_include_directories(nunicode SYSTEM PUBLIC
- ${CMAKE_SOURCE_DIR}/vendor/nunicode/include
+target_include_directories(mbgl-vendor-nunicode SYSTEM PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}/nunicode/include
)
diff --git a/vendor/pixelmatch-cpp b/vendor/pixelmatch-cpp
deleted file mode 160000
-Subproject 61f433cb485d6b08dc7fe97ae5f8717007c7bda
diff --git a/vendor/pixelmatch-cpp-files.json b/vendor/pixelmatch-cpp-files.json
deleted file mode 100644
index 0e2b9caaa5..0000000000
--- a/vendor/pixelmatch-cpp-files.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "mapbox/pixelmatch.hpp": "vendor/pixelmatch-cpp/include/mapbox/pixelmatch.hpp"
- },
- "private_headers": {}
-}
diff --git a/vendor/pixelmatch-cpp.cmake b/vendor/pixelmatch-cpp.cmake
deleted file mode 100644
index 8efa527133..0000000000
--- a/vendor/pixelmatch-cpp.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-add_library(pixelmatch-cpp INTERFACE)
-
-target_include_directories(pixelmatch-cpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/pixelmatch-cpp/include
-)
diff --git a/vendor/polylabel.cmake b/vendor/polylabel.cmake
index d732723a89..5a930c9f6d 100644
--- a/vendor/polylabel.cmake
+++ b/vendor/polylabel.cmake
@@ -1,5 +1,9 @@
-add_library(polylabel INTERFACE)
+if(TARGET mbgl-vendor-polylabel)
+ return()
+endif()
-target_include_directories(polylabel SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/polylabel/include
+add_library(mbgl-vendor-polylabel INTERFACE)
+
+target_include_directories(mbgl-vendor-polylabel SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/polylabel/include
)
diff --git a/vendor/protozero.cmake b/vendor/protozero.cmake
index e4f32f1ace..41af37f188 100644
--- a/vendor/protozero.cmake
+++ b/vendor/protozero.cmake
@@ -1,5 +1,9 @@
-add_library(protozero INTERFACE)
+if(TARGET mbgl-vendor-protozero)
+ return()
+endif()
-target_include_directories(protozero SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/protozero/include
+add_library(mbgl-vendor-protozero INTERFACE)
+
+target_include_directories(mbgl-vendor-protozero SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/protozero/include
)
diff --git a/vendor/rapidjson b/vendor/rapidjson
deleted file mode 160000
-Subproject f54b0e47a08782a6131cc3d60f94d038fa6e0a5
diff --git a/vendor/rapidjson-files.json b/vendor/rapidjson-files.json
deleted file mode 100644
index 1d3a454083..0000000000
--- a/vendor/rapidjson-files.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "rapidjson/allocators.h": "vendor/rapidjson/include/rapidjson/allocators.h",
- "rapidjson/document.h": "vendor/rapidjson/include/rapidjson/document.h",
- "rapidjson/encodedstream.h": "vendor/rapidjson/include/rapidjson/encodedstream.h",
- "rapidjson/encodings.h": "vendor/rapidjson/include/rapidjson/encodings.h",
- "rapidjson/error/en.h": "vendor/rapidjson/include/rapidjson/error/en.h",
- "rapidjson/error/error.h": "vendor/rapidjson/include/rapidjson/error/error.h",
- "rapidjson/filereadstream.h": "vendor/rapidjson/include/rapidjson/filereadstream.h",
- "rapidjson/filewritestream.h": "vendor/rapidjson/include/rapidjson/filewritestream.h",
- "rapidjson/fwd.h": "vendor/rapidjson/include/rapidjson/fwd.h",
- "rapidjson/internal/biginteger.h": "vendor/rapidjson/include/rapidjson/internal/biginteger.h",
- "rapidjson/internal/diyfp.h": "vendor/rapidjson/include/rapidjson/internal/diyfp.h",
- "rapidjson/internal/dtoa.h": "vendor/rapidjson/include/rapidjson/internal/dtoa.h",
- "rapidjson/internal/ieee754.h": "vendor/rapidjson/include/rapidjson/internal/ieee754.h",
- "rapidjson/internal/itoa.h": "vendor/rapidjson/include/rapidjson/internal/itoa.h",
- "rapidjson/internal/meta.h": "vendor/rapidjson/include/rapidjson/internal/meta.h",
- "rapidjson/internal/pow10.h": "vendor/rapidjson/include/rapidjson/internal/pow10.h",
- "rapidjson/internal/regex.h": "vendor/rapidjson/include/rapidjson/internal/regex.h",
- "rapidjson/internal/stack.h": "vendor/rapidjson/include/rapidjson/internal/stack.h",
- "rapidjson/internal/strfunc.h": "vendor/rapidjson/include/rapidjson/internal/strfunc.h",
- "rapidjson/internal/strtod.h": "vendor/rapidjson/include/rapidjson/internal/strtod.h",
- "rapidjson/internal/swap.h": "vendor/rapidjson/include/rapidjson/internal/swap.h",
- "rapidjson/istreamwrapper.h": "vendor/rapidjson/include/rapidjson/istreamwrapper.h",
- "rapidjson/memorybuffer.h": "vendor/rapidjson/include/rapidjson/memorybuffer.h",
- "rapidjson/memorystream.h": "vendor/rapidjson/include/rapidjson/memorystream.h",
- "rapidjson/msinttypes/inttypes.h": "vendor/rapidjson/include/rapidjson/msinttypes/inttypes.h",
- "rapidjson/msinttypes/stdint.h": "vendor/rapidjson/include/rapidjson/msinttypes/stdint.h",
- "rapidjson/ostreamwrapper.h": "vendor/rapidjson/include/rapidjson/ostreamwrapper.h",
- "rapidjson/pointer.h": "vendor/rapidjson/include/rapidjson/pointer.h",
- "rapidjson/prettywriter.h": "vendor/rapidjson/include/rapidjson/prettywriter.h",
- "rapidjson/rapidjson.h": "vendor/rapidjson/include/rapidjson/rapidjson.h",
- "rapidjson/reader.h": "vendor/rapidjson/include/rapidjson/reader.h",
- "rapidjson/schema.h": "vendor/rapidjson/include/rapidjson/schema.h",
- "rapidjson/stream.h": "vendor/rapidjson/include/rapidjson/stream.h",
- "rapidjson/stringbuffer.h": "vendor/rapidjson/include/rapidjson/stringbuffer.h",
- "rapidjson/writer.h": "vendor/rapidjson/include/rapidjson/writer.h"
- },
- "private_headers": {}
-}
diff --git a/vendor/rapidjson.cmake b/vendor/rapidjson.cmake
deleted file mode 100644
index 40b5c31895..0000000000
--- a/vendor/rapidjson.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-add_library(rapidjson INTERFACE)
-
-target_include_directories(rapidjson SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/rapidjson/include
-)
-
-target_compile_definitions(rapidjson INTERFACE
- RAPIDJSON_HAS_STDSTRING=1
-)
-
-if(WIN32)
- target_compile_definitions(rapidjson INTERFACE
- RAPIDJSON_HAS_CXX11_RVALUE_REFS
- )
-endif()
diff --git a/vendor/shelf-pack-cpp.cmake b/vendor/shelf-pack-cpp.cmake
index 0d8e850723..bf2e07c22f 100644
--- a/vendor/shelf-pack-cpp.cmake
+++ b/vendor/shelf-pack-cpp.cmake
@@ -1,5 +1,9 @@
-add_library(shelf-pack-cpp INTERFACE)
+if(TARGET mbgl-vendor-shelf-pack-cpp)
+ return()
+endif()
-target_include_directories(shelf-pack-cpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/shelf-pack-cpp/include
+add_library(mbgl-vendor-shelf-pack-cpp INTERFACE)
+
+target_include_directories(mbgl-vendor-shelf-pack-cpp SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/shelf-pack-cpp/include
)
diff --git a/vendor/sqlite.cmake b/vendor/sqlite.cmake
new file mode 100644
index 0000000000..d01ac0dd4e
--- /dev/null
+++ b/vendor/sqlite.cmake
@@ -0,0 +1,26 @@
+if(TARGET mbgl-vendor-sqlite)
+ return()
+endif()
+
+add_library(mbgl-vendor-sqlite STATIC
+ ${CMAKE_CURRENT_LIST_DIR}/sqlite/src/sqlite3.c
+)
+
+include(CheckSymbolExists)
+check_symbol_exists("strerror_r" "string.h" MBGL_SQLITE3_HAVE_STRERROR_R)
+
+if(MBGL_SQLITE3_HAVE_STRERROR_R)
+ target_compile_definitions(mbgl-vendor-sqlite PRIVATE
+ HAVE_STRERROR_R
+ )
+endif()
+
+# So we don't need to link with -ldl
+target_compile_definitions(mbgl-vendor-sqlite PRIVATE
+ SQLITE_OMIT_LOAD_EXTENSION
+ SQLITE_THREADSAFE
+)
+
+target_include_directories(mbgl-vendor-sqlite SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/sqlite/include
+)
diff --git a/vendor/supercluster.hpp b/vendor/supercluster.hpp
deleted file mode 160000
-Subproject 274ec138306c7b110bf4dde47706aeb43dc8147
diff --git a/vendor/supercluster.hpp-files.json b/vendor/supercluster.hpp-files.json
deleted file mode 100644
index 775dda8776..0000000000
--- a/vendor/supercluster.hpp-files.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "//": "This file is generated. Do not edit. Regenerate it with scripts/generate-file-lists.js",
- "sources": [],
- "public_headers": {
- "supercluster.hpp": "vendor/supercluster.hpp/include/supercluster.hpp"
- },
- "private_headers": {}
-}
diff --git a/vendor/supercluster.hpp.cmake b/vendor/supercluster.hpp.cmake
deleted file mode 100644
index 90d0becade..0000000000
--- a/vendor/supercluster.hpp.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-add_library(supercluster.hpp INTERFACE)
-
-target_include_directories(supercluster.hpp SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/supercluster.hpp/include
-)
diff --git a/vendor/unique_resource.cmake b/vendor/unique_resource.cmake
index ad450066eb..02fed9cbc1 100644
--- a/vendor/unique_resource.cmake
+++ b/vendor/unique_resource.cmake
@@ -1,5 +1,9 @@
-add_library(unique_resource INTERFACE)
+if(TARGET mbgl-vendor-unique_resource)
+ return()
+endif()
-target_include_directories(unique_resource SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/unique_resource
+add_library(mbgl-vendor-unique_resource INTERFACE)
+
+target_include_directories(mbgl-vendor-unique_resource SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/unique_resource
)
diff --git a/vendor/vector-tile.cmake b/vendor/vector-tile.cmake
index ec4552afcc..5f31fae94c 100644
--- a/vendor/vector-tile.cmake
+++ b/vendor/vector-tile.cmake
@@ -1,5 +1,9 @@
-add_library(vector-tile INTERFACE)
+if(TARGET mbgl-vendor-vector-tile)
+ return()
+endif()
-target_include_directories(vector-tile SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/vector-tile/include
+add_library(mbgl-vendor-vector-tile INTERFACE)
+
+target_include_directories(mbgl-vendor-vector-tile SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/vector-tile/include
)
diff --git a/vendor/wagyu.cmake b/vendor/wagyu.cmake
index 7cf3397b89..15f0758fb8 100644
--- a/vendor/wagyu.cmake
+++ b/vendor/wagyu.cmake
@@ -1,5 +1,9 @@
-add_library(wagyu INTERFACE)
+if(TARGET mbgl-vendor-wagyu)
+ return()
+endif()
-target_include_directories(wagyu SYSTEM INTERFACE
- ${CMAKE_SOURCE_DIR}/vendor/wagyu/include
+add_library(mbgl-vendor-wagyu INTERFACE)
+
+target_include_directories(mbgl-vendor-wagyu SYSTEM INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/wagyu/include
)