summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorm-stephen <truestyle2005@163.com>2019-09-17 10:46:33 +0800
committerGitHub <noreply@github.com>2019-09-17 10:46:33 +0800
commiteb1f8eb32286dd7b89ae76b839b774b0071ca5a5 (patch)
tree0db6df1811ffb8c9a5139687817386dee23cfef1
parentb0018568b84f9bb1c185f0441a41741e9fde5a84 (diff)
parenta99240a54ffd5eb77568d41e10186411b1b759eb (diff)
downloadqtlocation-mapboxgl-eb1f8eb32286dd7b89ae76b839b774b0071ca5a5.tar.gz
Merge branch 'master' into stephen-ios-error-report
-rw-r--r--.github/stale.yml4
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules24
-rw-r--r--CMakeLists.txt9
-rw-r--r--Makefile10
-rw-r--r--bin/offline.cpp2
-rw-r--r--circle.yml33
-rw-r--r--cmake/core.cmake6
-rw-r--r--cmake/expression-test.cmake27
-rw-r--r--cmake/glfw.cmake2
-rw-r--r--cmake/offline.cmake2
-rw-r--r--cmake/render-test.cmake9
-rw-r--r--cmake/render.cmake2
-rw-r--r--cmake/test.cmake4
-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/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/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.hpp7
-rw-r--r--include/mbgl/util/bitmask_operations.hpp33
-rw-r--r--include/mbgl/util/peer.hpp34
m---------mapbox-gl-js0
-rw-r--r--misc/buck/mapbox-gl-native/BUCK6
-rw-r--r--package.json2
-rw-r--r--platform/android/.gitignore2
-rw-r--r--platform/android/CHANGELOG.md101
-rw-r--r--platform/android/LICENSE.md4
-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/http/HttpRequestUrl.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java2
-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/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.java81
-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.java5
-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.java7
-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/PropertyFactory.java12
-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.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml2
-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/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.java18
-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/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/utils/TestConstants.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/FontUtilsTest.java2
-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/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_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
-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.hpp3
-rwxr-xr-xplatform/android/src/jni_native.cpp164
-rw-r--r--platform/android/src/jni_native.hpp11
-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/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/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
m---------platform/android/vendor/mapbox-gestures-android0
m---------platform/android/vendor/mapbox-java0
-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.h34
-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/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.mm22
-rw-r--r--platform/darwin/src/MGLStyleLayer_Private.h1
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h13
-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.mm19
-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/MGLResourceTests.mm23
-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/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.cpp4
-rw-r--r--platform/ios/CHANGELOG.md47
-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.swift5
-rw-r--r--platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m111
-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/MBXState.h2
-rw-r--r--platform/ios/app/MBXState.m9
-rw-r--r--platform/ios/app/MBXViewController.m53
-rw-r--r--platform/ios/app/Main.storyboard14
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj64
-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.mm9
-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.h28
-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.mm18
-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/linux/README.md4
-rw-r--r--platform/linux/config.cmake8
-rw-r--r--platform/macos/CHANGELOG.md11
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj8
-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/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.md15
-rw-r--r--platform/node/DEVELOPING.md2
-rw-r--r--platform/node/test/ignores.json26
-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--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/main.cpp36
-rw-r--r--render-test/metadata.hpp35
-rw-r--r--render-test/parser.cpp127
-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-cxx11abi.datbin8780 -> 0 bytes
-rwxr-xr-xscripts/check-cxx11abi.sh21
-rwxr-xr-xscripts/generate-file-lists.js23
-rw-r--r--src/core-files.json2
-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_instance.cpp47
-rw-r--r--src/mbgl/layout/symbol_instance.hpp29
-rw-r--r--src/mbgl/layout/symbol_layout.cpp228
-rw-r--r--src/mbgl/layout/symbol_layout.hpp18
-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.cpp11
-rw-r--r--src/mbgl/map/transform.hpp2
-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/symbol_bucket.cpp101
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp47
-rw-r--r--src/mbgl/renderer/image_manager.cpp1
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp5
-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.cpp5
-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/geojson_options.cpp69
-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/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.cpp141
-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.cpp1
-rw-r--r--src/mbgl/style/style_impl.cpp5
-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.hpp21
-rw-r--r--src/mbgl/text/glyph_manager.cpp7
-rw-r--r--src/mbgl/text/placement.cpp304
-rw-r--r--src/mbgl/text/placement.hpp13
-rw-r--r--src/mbgl/text/quads.cpp48
-rw-r--r--src/mbgl/text/quads.hpp4
-rw-r--r--src/mbgl/text/shaping.cpp49
-rw-r--r--src/mbgl/text/shaping.hpp9
-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/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/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.cpp38
-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/test-files.json2
-rw-r--r--test/text/cross_tile_symbol_index.test.cpp29
-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.cpp2
-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
m---------vendor/filesystem0
-rw-r--r--vendor/filesystem-files.json13
-rw-r--r--vendor/filesystem.cmake5
m---------vendor/geojson.hpp0
-rw-r--r--vendor/geojson.hpp-files.json10
-rw-r--r--vendor/geojson.hpp.cmake5
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
m---------vendor/pixelmatch-cpp0
-rw-r--r--vendor/pixelmatch-cpp-files.json8
-rw-r--r--vendor/pixelmatch-cpp.cmake5
m---------vendor/rapidjson0
-rw-r--r--vendor/rapidjson-files.json42
-rw-r--r--vendor/rapidjson.cmake15
m---------vendor/supercluster.hpp0
-rw-r--r--vendor/supercluster.hpp-files.json8
-rw-r--r--vendor/supercluster.hpp.cmake5
418 files changed, 8646 insertions, 4269 deletions
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 9002542eee..916c01fed0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,5 @@
export BUILDTYPE ?= Debug
export IS_LOCAL_DEVELOPMENT ?= true
-export WITH_CXX11ABI ?= $(shell scripts/check-cxx11abi.sh)
export TARGET_BRANCH ?= master
CMAKE ?= cmake
@@ -378,7 +377,6 @@ $(LINUX_BUILD): $(BUILD_DEPS)
mkdir -p $(LINUX_OUTPUT_PATH)
(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})
@@ -476,7 +474,6 @@ $(QT_BUILD): $(BUILD_DEPS)
-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)
@@ -491,7 +488,6 @@ $(MACOS_QT_PROJ_PATH): $(BUILD_DEPS)
-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
@@ -518,7 +514,6 @@ $(QNX_QT_BUILD): $(BUILD_DEPS)
-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
@@ -560,7 +555,6 @@ test-node: node
npm test
npm run test-query
npm run test-memory
- npm run test-expressions
#### Android targets ###########################################################
@@ -801,7 +795,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
@@ -810,7 +804,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/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 68c49dd3f6..453aab85ac 100644
--- a/circle.yml
+++ b/circle.yml
@@ -260,7 +260,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:
@@ -272,6 +272,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:
@@ -435,6 +440,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:
@@ -493,7 +511,7 @@ commands:
- run:
name: Check if this job can be skipped
command: |
- if [[ $CIRCLE_BRANCH != master ]] && [[ $CIRCLE_BRANCH != release-* ]]; then
+ if [[ $CIRCLE_BRANCH != master ]] && [[ $CIRCLE_BRANCH != release-* ]] && [[ -z $CIRCLE_TAG ]]; then
scripts/check-ci-job-skippability.js
fi
@@ -780,7 +798,6 @@ jobs:
JOBS: 4
BUILDTYPE: RelWithDebInfo
WITH_EGL: 1
- WITH_CXX11ABI: 0
steps:
- install-dependencies
- build-node
@@ -837,7 +854,6 @@ jobs:
JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
- WITH_CXX11ABI: 1
steps:
- install-dependencies
- check-if-this-job-can-be-skipped
@@ -902,7 +918,6 @@ jobs:
JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
- WITH_CXX11ABI: 0
DISPLAY: :0
steps:
- install-dependencies
@@ -988,9 +1003,12 @@ jobs:
- 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
# ------------------------------------------------------------------------------
@@ -1272,9 +1290,12 @@ jobs:
- 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
# ------------------------------------------------------------------------------
@@ -1295,7 +1316,7 @@ jobs:
# ------------------------------------------------------------------------------
qt5-linux-gcc5-release:
docker:
- - image: mbgl/linux-gcc-5-qt-5.9:5132cfd29f
+ - image: mbgl/linux-gcc-5-qt-5.9:5cd92d7d1c
resource_class: large
working_directory: /src
environment:
diff --git a/cmake/core.cmake b/cmake/core.cmake
index abb55d174a..f08ff6bcab 100644
--- a/cmake/core.cmake
+++ b/cmake/core.cmake
@@ -12,9 +12,7 @@ target_link_libraries(mbgl-core PRIVATE
eternal
expected
geojson-vt-cpp
- kdbush.hpp
shelf-pack-cpp
- supercluster.hpp
unique_resource
wagyu
)
@@ -28,12 +26,12 @@ endif()
# libraries in our public interface.
target_link_libraries(mbgl-core PUBLIC
boost
+ mapbox-base-extras
mapbox-base
- geojson.hpp
${ICU_LIBRARY}
polylabel
protozero
- rapidjson
+ Mapbox::Base::Extras::rapidjson
vector-tile
)
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/glfw.cmake b/cmake/glfw.cmake
index a4d9c30477..6abc93d837 100644
--- a/cmake/glfw.cmake
+++ b/cmake/glfw.cmake
@@ -23,7 +23,7 @@ target_link_libraries(mbgl-glfw
PRIVATE mbgl-core
PRIVATE glfw
PRIVATE cheap-ruler-cpp
- PRIVATE args
+ PRIVATE Mapbox::Base::Extras::args
)
mbgl_platform_glfw()
diff --git a/cmake/offline.cmake b/cmake/offline.cmake
index 3fa075f07b..1f5cc60f70 100644
--- a/cmake/offline.cmake
+++ b/cmake/offline.cmake
@@ -12,7 +12,7 @@ target_include_directories(mbgl-offline
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..9bc37df808 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
+ Mapbox::Base::Extras::args
expected
- filesystem
- pixelmatch-cpp
- rapidjson
+ 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..3cb0ef3a72 100644
--- a/cmake/test.cmake
+++ b/cmake/test.cmake
@@ -24,11 +24,11 @@ target_include_directories(mbgl-test
target_link_libraries(mbgl-test PRIVATE
googletest
- args
+ Mapbox::Base::Extras::args
mbgl-core
shelf-pack-cpp
unique_resource
- pixelmatch-cpp
+ 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/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/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..ece7f5615a 100644
--- a/include/mbgl/style/sources/vector_source.hpp
+++ b/include/mbgl/style/sources/vector_source.hpp
@@ -10,7 +10,7 @@ class AsyncRequest;
namespace style {
-class VectorSource : public Source {
+class VectorSource final : public Source {
public:
VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset);
~VectorSource() final;
@@ -23,9 +23,14 @@ 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};
};
template <>
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/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/mapbox-gl-js b/mapbox-gl-js
-Subproject f818169d0904f09c02af0f668de5a2be2537dd9
+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/package.json b/package.json
index acd2eb80bf..7b30d6a61a 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",
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 08c2e56892..a7fd332b65 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -3,23 +3,110 @@
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
## master
-- Enable variable label placement when `text-allow-overlap` property is set to true [#15354](https://github.com/mapbox/mapbox-gl-native/pull/15354).
-- 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.
+
+### 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)
+
+## 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
- - Add fallback support to local ideograph font families [#15255](https://github.com/mapbox/mapbox-gl-native/pull/15255)
+ - 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)
+ - 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):
@@ -42,7 +129,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 18e5cb41c3..8423a34a6a 100644
--- a/platform/android/LICENSE.md
+++ b/platform/android/LICENSE.md
@@ -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/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..46de3673e4 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
@@ -1182,7 +1182,6 @@ public final class LocationComponent {
}
isLayerReady = false;
- locationLayerController.hide();
staleStateManager.onStop();
if (compassEngine != null) {
updateCompassListenerState(false);
@@ -1289,6 +1288,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/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 f922117dd0..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;
@@ -70,7 +71,11 @@ 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;
private boolean textureMode;
@@ -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 = MapboxConstants.DEFAULT_FONT;
+ 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,6 +657,18 @@ 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>
@@ -944,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;
}
@@ -962,11 +1004,22 @@ 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 {@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;
}
/**
@@ -1029,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));
@@ -1114,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;
}
@@ -1124,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;
}
@@ -1171,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 9e72be8e9b..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
@@ -417,10 +417,6 @@ public final class UiSettings {
}
void update(@NonNull CameraPosition cameraPosition) {
- if (!isCompassEnabled()) {
- return;
- }
-
double clockwiseBearing = -cameraPosition.bearing;
compassView.update(clockwiseBearing);
}
@@ -1073,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 4c70d13b17..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
@@ -339,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);
}
/**
@@ -625,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/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
index 82085b171c..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[]
@@ -2476,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[]
@@ -2486,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/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
index 09064ee168..627b342179 100644
--- 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
@@ -1,7 +1,8 @@
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;
@@ -18,6 +19,14 @@ import static com.mapbox.mapboxsdk.constants.MapboxConstants.DEFAULT_FONT;
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
@@ -34,7 +43,13 @@ public class FontUtils {
return null;
}
- List<String> validFonts = getAvailableFonts();
+ 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;
@@ -47,16 +62,18 @@ public class FontUtils {
return DEFAULT_FONT;
}
- private static List<String> getAvailableFonts() {
+ @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 f = Typeface.class.getDeclaredField("sSystemFontMap");
- f.setAccessible(true);
- Map<String, Typeface> fontMap = (Map<String, Typeface>) f.get(typeface);
+ 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);
+ Logger.e(TAG, "Couldn't load fonts from Typeface", exception);
MapStrictMode.strictModeViolation("Couldn't load fonts from Typeface", exception);
}
return fonts;
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/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/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 cfcf0a0486..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 {
@@ -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/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/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
index 694215d16f..fa068cb973 100644
--- 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
@@ -1,9 +1,7 @@
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;
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/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_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 526861e8b1..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.5.0',
+ 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/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..cb519f911d 100644
--- a/platform/android/src/jni.hpp
+++ b/platform/android/src/jni.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <string>
+#include <jni/jni.hpp>
typedef struct _jmethodID* jmethodID;
typedef struct _JavaVM JavaVM;
@@ -17,7 +18,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..a4c89d3036
--- /dev/null
+++ b/platform/android/src/jni_native.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "jni.hpp"
+
+namespace mbgl {
+namespace android {
+
+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/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/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/vendor/mapbox-gestures-android b/platform/android/vendor/mapbox-gestures-android
-Subproject d099f39e00358d6700c3e1c588e08e1a80f6c45
+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/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..0f20cf1bb2 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,16 @@ 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 queue The queue to handle the result on.
+ @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 9ba0cbebfe..85619a780b 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
@@ -227,12 +255,12 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
[[MMEEventsManager sharedManager] reportError:error];
#endif
-
// Dispatch to result queue
dispatch_async(queue, ^{
strongSelf.completion(nil, error);
strongSelf.completion = nil;
});
+
} else {
#if TARGET_OS_IPHONE
MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image) scale:strongSelf.options.scale];
@@ -241,11 +269,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
@@ -253,7 +282,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];
@@ -295,7 +324,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();
@@ -362,6 +407,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];
}
@@ -382,8 +442,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;
@@ -397,19 +457,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;
+ }
}
});
});
@@ -630,7 +701,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/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 62308ce5e1..1d7b46e1cc 100644
--- a/platform/darwin/src/MGLSource.mm
+++ b/platform/darwin/src/MGLSource.mm
@@ -11,6 +11,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
@@ -23,13 +25,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;
}
@@ -37,13 +40,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);
@@ -63,6 +71,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.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_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 fb00b0d165..53bc488a41 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -1404,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:
@@ -1428,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:
@@ -1521,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.
@@ -1634,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
diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h
index df3c61a61c..963eda384b 100644
--- a/platform/darwin/src/MGLTypes.h
+++ b/platform/darwin/src/MGLTypes.h
@@ -56,8 +56,10 @@ typedef NS_ENUM(NSInteger, MGLErrorCode) {
MGLErrorCodeSourceIdentifierMismatch = 8,
/** An error occurred while modifying the offline storage database */
MGLErrorCodeModifyingOfflineStorageFailed = 9,
+ /** Source is invalid and cannot be removed from the style (e.g. after a style change) */
+ MGLErrorCodeSourceCannotBeRemovedFromStyle = 10,
/** An error occurred while rendering */
- MGLErrorCodeRenderingError = 10,
+ MGLErrorCodeRenderingError = 11,
};
/** 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 5737d8b999..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) {
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/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/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/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..f58d6e100a 100644
--- a/platform/default/src/mbgl/util/format_number.cpp
+++ b/platform/default/src/mbgl/util/format_number.cpp
@@ -23,7 +23,11 @@ std::string formatNumber(double number, const std::string& localeId, const std::
.toString();
} else {
ustr = icu::number::NumberFormatter::with()
+#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();
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 47da8ac53e..8515fd467f 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -2,7 +2,45 @@
Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
-## 5.3.0
+## 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))
+
+### Performance improvements
+
+* 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
@@ -11,11 +49,18 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* 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
+* 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..c3400b1fa2 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")
diff --git a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m
index 32e5fc782d..7707896203 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,62 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates
[self waitForExpectations:@[expectation] timeout:10.0];
}
+- (void)testSnapshotWithOverlayHandlerFailure {
+ if (![self validAccessToken]) {
+ return;
+ }
+
+ 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 * _Nullable snapshotOverlay) {
+ UIGraphicsEndImageContext();
+ [expectation fulfill];
+ } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
+ XCTAssertNil(snapshot);
+ XCTAssertNotNil(error);
+ [expectation fulfill];
+ }];
+
+ [self waitForExpectations:@[expectation] timeout:10.0];
+}
+
+- (void)testSnapshotWithOverlayHandlerSuccess {
+ if (![self validAccessToken]) {
+ return;
+ }
+
+ 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);
+
+ [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay * _Nullable snapshotOverlay) {
+ 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 77ff18c104..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.3'
+ 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 5c8b30220a..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.3'
+ 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 c356a3c12d..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.3'
+ 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/MBXState.h b/platform/ios/app/MBXState.h
index 7cf064acd8..346c31d586 100644
--- a/platform/ios/app/MBXState.h
+++ b/platform/ios/app/MBXState.h
@@ -12,7 +12,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 +25,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/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 2fb95e1b17..346a182a88 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -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];
@@ -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/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index de2bec5d99..7712c332c4 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -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 */; };
@@ -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>"; };
@@ -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>";
@@ -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..acb25a560c 100644
--- a/platform/ios/src/MGLCompassButton.mm
+++ b/platform/ios/src/MGLCompassButton.mm
@@ -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/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 7b4277d407..0b9ab75699 100644
--- a/platform/ios/src/MGLMapView+Impl.mm
+++ b/platform/ios/src/MGLMapView+Impl.mm
@@ -75,8 +75,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 e6f66e39ae..017ba525c4 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -748,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
@@ -762,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.
@@ -777,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.
@@ -794,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.
@@ -1065,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
@@ -1081,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
@@ -1100,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
@@ -1116,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
@@ -1306,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..cd8bc1d030 100644
--- a/platform/ios/src/MGLScaleBar.mm
+++ b/platform/ios/src/MGLScaleBar.mm
@@ -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/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/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..d7bfad0011 100644
--- a/platform/linux/config.cmake
+++ b/platform/linux/config.cmake
@@ -101,14 +101,6 @@ macro(mbgl_platform_core)
PRIVATE 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 d5092be1db..da9a179ad3 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,8 +10,14 @@
* 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)
-* Fixed an issue where animated camera transitions zoomed in or out too dramatically. [#15281](https://github.com/mapbox/mapbox-gl-native/pull/15281)
+* `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))
### Styles and rendering
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 45e5a6d653..d290d97012 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -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 */; };
@@ -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>"; };
@@ -799,7 +799,7 @@
DA8F25911D51CA750010E6B5 /* MGLSymbolStyleLayer.h */,
DA8F25921D51CA750010E6B5 /* MGLSymbolStyleLayer.mm */,
DA6408D51DA4E5DA00908C90 /* MGLVectorStyleLayer.h */,
- DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.m */,
+ DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.mm */,
);
name = Layers;
sourceTree = "<group>";
@@ -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/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 8271e5fcfb..519f30aaad 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,14 +1,19 @@
-# master
+# 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).
# 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/test/ignores.json b/platform/node/test/ignores.json
index 7665972aad..d0ae1d0b35 100644
--- a/platform/node/test/ignores.json
+++ b/platform/node/test/ignores.json
@@ -40,12 +40,9 @@
"render-tests/custom-layer-js/tent-3d": "skip - js specific",
"render-tests/regressions/mapbox-gl-js#7708": "skip - js specific",
"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",
@@ -73,19 +70,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/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601",
"render-tests/background-color/colorSpace-hcl": "needs issue",
"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",
@@ -103,17 +93,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",
- "render-tests/icon-text-fit/both-padding": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/icon-text-fit/both": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/icon-text-fit/height-padding": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/icon-text-fit/height": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/icon-text-fit/placement-line": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/icon-text-fit/width-padding": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/icon-text-fit/width": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/regressions/mapbox-gl-js#5631": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/text-variable-anchor/all-anchors-icon-text-fit": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/text-variable-anchor/icon-text-fit-collision-box": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit": "https://github.com/mapbox/mapbox-gl-native/issues/15346",
- "render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode-icon-text-fit": "https://github.com/mapbox/mapbox-gl-native/issues/15346"
+ "query-tests/regressions/mapbox-gl-js#7883": "https://github.com/mapbox/mapbox-gl-native/issues/14585"
}
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/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/main.cpp b/render-test/main.cpp
index 35f0cdea30..3ab48fe5b2 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,33 @@
#define ANSI_COLOR_LIGHT_GRAY "\x1b[90m"
#define ANSI_COLOR_RESET "\x1b[0m"
+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);
+}
+
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 +73,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 +92,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 fd048f08c6..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,24 +279,50 @@ 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)
};
}
@@ -288,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;
}
@@ -303,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 5bd855c43a..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.1285); // Defined in GL JS
+ 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-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/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..9453e504bb 100644
--- a/src/core-files.json
+++ b/src/core-files.json
@@ -456,6 +456,7 @@
"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",
@@ -479,7 +480,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_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index 113bf5bd35..41a00e0131 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -21,15 +21,18 @@ 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,
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;
@@ -69,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,
@@ -83,13 +87,12 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
const float overscaling,
const float iconRotation,
const float textRotation,
- float radialTextOffset_,
- bool allowVerticalPlacement) :
+ 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, textRotation),
@@ -101,12 +104,21 @@ 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();
@@ -152,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 2c6aad653f..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,8 +25,8 @@ 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,
@@ -39,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 {
@@ -47,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,
@@ -61,8 +70,9 @@ public:
const float overscaling,
const float iconRotation,
const float textRotation,
- float radialTextOffset,
- bool allowVerticalPlacement);
+ const std::array<float, 2>& variableTextOffset,
+ bool allowVerticalPlacement,
+ const SymbolContent iconType = SymbolContent::None);
optional<size_t> getDefaultHorizontalPlacedTextIndex() const;
const GeometryCoordinates& line() const;
@@ -70,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:
@@ -78,8 +92,7 @@ private:
public:
Anchor anchor;
- bool hasText;
- bool hasIcon;
+ SymbolContent symbolContent;
std::size_t rightJustifiedGlyphQuadsSize;
std::size_t centerJustifiedGlyphQuadsSize;
@@ -89,6 +102,7 @@ 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
@@ -101,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 dbf209b414..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);
@@ -235,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;
@@ -247,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;
@@ -266,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;
@@ -285,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;
@@ -296,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) {
@@ -320,18 +368,18 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
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);
@@ -366,7 +414,7 @@ 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;
}
@@ -398,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;
@@ -419,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();
@@ -431,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);
@@ -459,7 +509,15 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float textMaxAngle = layout->get<TextMaxAngle>() * util::DEG2RAD;
const float iconRotation = layout->evaluate<IconRotate>(zoom, feature);
const float textRotation = layout->evaluate<TextRotate>(zoom, feature);
- const float radialTextOffset = layout->evaluate<TextRadialOffset>(zoom, feature) * util::ONE_EM;
+ 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>();
@@ -467,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);
@@ -478,18 +552,19 @@ 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, iconRotation, textRotation, radialTextOffset, allowVerticalPlacement);
+ overscaling, iconRotation, textRotation, variableTextOffset, allowVerticalPlacement, iconType);
}
};
const auto createSymbolInstanceSharedData = [&] (GeometryCoordinates line) {
return std::make_shared<SymbolInstanceSharedData>(std::move(line),
- shapedTextOrientations, shapedIcon, evaluatedLayoutProperties, layoutTextSize,
+ shapedTextOrientations, shapedIcon, verticallyShapedIcon, evaluatedLayoutProperties,
textPlacement, textOffset, glyphPositions, allowVerticalPlacement);
};
@@ -603,20 +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,
+ 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) {
@@ -643,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();
}
@@ -693,9 +778,9 @@ 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;
@@ -782,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};
@@ -799,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();
@@ -820,24 +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.textCollisionFeature, true /*isText*/);
if (symbolInstance.verticalTextCollisionFeature) {
- populateCollisionBox(*symbolInstance.verticalTextCollisionFeature);
+ populateCollisionBox(*symbolInstance.verticalTextCollisionFeature, true /*isText*/);
+ }
+ if (symbolInstance.verticalIconCollisionFeature) {
+ populateCollisionBox(*symbolInstance.verticalIconCollisionFeature, false /*isText*/);
}
- populateCollisionBox(symbolInstance.iconCollisionFeature);
+ populateCollisionBox(symbolInstance.iconCollisionFeature, false /*isText*/);
}
}
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 70a3482644..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,7 +103,6 @@ private:
const uint32_t tileSize;
const float tilePixelRatio;
- bool sdfIcons = false;
bool iconsNeedLinear = false;
bool sortFeaturesByY = false;
bool allowVerticalPlacement = false;
@@ -101,6 +110,7 @@ private:
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/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 d386898c3b..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);
}
@@ -96,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;
}
@@ -172,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;
}
@@ -274,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;
}
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index ffeff3859c..30ce8a37a4 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -29,7 +29,7 @@ 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&);
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/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index 21a7870473..ff201e9c2c 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -18,7 +18,6 @@ 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_,
@@ -28,7 +27,6 @@ SymbolBucket::SymbolBucket(Immutable<style::SymbolLayoutProperties::PossiblyEval
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),
@@ -87,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;
@@ -149,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 {
@@ -160,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) {
@@ -188,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;
}
@@ -199,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());
@@ -225,8 +257,13 @@ 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]);
}
}
}
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index f47ced8331..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;
@@ -43,6 +43,9 @@ public:
// 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 {
@@ -52,7 +55,6 @@ public:
const style::PropertyValue<float>& textSize,
const style::PropertyValue<float>& iconSize,
float zoom,
- bool sdfIcons,
bool iconsNeedLinear,
bool sortFeaturesByY,
const std::string bucketLeaderID,
@@ -69,8 +71,11 @@ 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;
@@ -83,7 +88,6 @@ public:
float sortedAngle = std::numeric_limits<float>::max();
// Flags
- const bool sdfIcons : 1;
const bool iconsNeedLinear : 1;
const bool sortFeaturesByY : 1;
bool staticUploaded : 1;
@@ -121,7 +125,8 @@ public:
std::unique_ptr<SymbolSizeBinder> iconSizeBinder;
Buffer icon;
-
+ Buffer sdfIcon;
+
struct CollisionBuffer {
gfx::VertexVector<gfx::Vertex<CollisionBoxLayoutAttributes>> vertices;
gfx::VertexVector<gfx::Vertex<CollisionBoxDynamicAttributes>> dynamicVertices;
@@ -135,22 +140,34 @@ 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& getOrCreateIconCollisionCircleBuffer() {
+ if (!iconCollisionCircle) iconCollisionCircle = std::make_unique<CollisionCircleBuffer>();
+ return *iconCollisionCircle;
+ }
- CollisionCircleBuffer& getOrCreateCollisionCircleBuffer() {
- if (!collisionCircle) collisionCircle = std::make_unique<CollisionCircleBuffer>();
- return *collisionCircle;
+ CollisionCircleBuffer& getOrCreateTextCollisionCircleBuffer() {
+ if (!textCollisionCircle) textCollisionCircle = std::make_unique<CollisionCircleBuffer>();
+ return *textCollisionCircle;
}
const float tilePixelRatio;
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 4725bce435..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()) {
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 6a134a398a..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()) {
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/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/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/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 4b335ead3c..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>
@@ -1101,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,
@@ -1133,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());
@@ -1406,47 +1407,47 @@ optional<Error> SymbolLayer::setLayoutProperty(const std::string& name, const Co
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) },
- { "text-writing-mode", static_cast<uint8_t>(Property::TextWritingMode) }
+ { "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());
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..f103a7768f 100644
--- a/src/mbgl/style/sources/vector_source.cpp
+++ b/src/mbgl/style/sources/vector_source.cpp
@@ -38,6 +38,7 @@ void VectorSource::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/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/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 234f718975..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>
@@ -99,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 27c4913c63..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,15 +86,13 @@ 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
@@ -152,12 +151,12 @@ 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 hasCollisionCircleData = bucket.hasCollisionCircleData();
+ const bool hasIconTextFit = layout.get<style::IconTextFit>() != style::IconTextFitType::None;
const bool zOrderByViewportY = layout.get<style::SymbolZOrder>() == style::SymbolZOrderType::ViewportY;
std::vector<ProjectedCollisionBox> textBoxes;
@@ -179,7 +178,9 @@ void Placement::placeBucket(
bool placeIcon = false;
bool offscreen = true;
std::pair<bool, bool> placed{ false, false };
- std::pair<bool, bool> placedVertical{ 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) {
const PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*horizontalTextIndex);
@@ -200,7 +201,7 @@ void Placement::placeBucket(
assert(!bucket.placementModes.empty());
for (auto& placementMode : bucket.placementModes) {
if (placementMode == style::TextWritingModeType::Vertical) {
- placedVertical = placed = placeVerticalFn();
+ placedVerticalText = placed = placeVerticalFn();
} else {
placed = placeHorizontalFn();
}
@@ -280,7 +281,7 @@ void Placement::placeBucket(
for (size_t i = 0u; i < placementAttempts; ++i) {
auto anchor = variableTextAnchors[i % anchorsSize];
const bool allowOverlap = (i >= anchorsSize);
- Point<float> shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale);
+ shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.variableTextOffset, textBoxScale);
if (rotateWithMap) {
float angle = pitchWithMap ? state.getBearing() : -state.getBearing();
shift = util::rotate(shift, angle);
@@ -311,7 +312,7 @@ void Placement::placeBucket(
}
variableOffsets.insert(std::make_pair(symbolInstance.crossTileID, VariableOffset{
- symbolInstance.radialTextOffset,
+ symbolInstance.variableTextOffset,
width,
height,
anchor,
@@ -359,21 +360,35 @@ void Placement::placeBucket(
}
if (symbolInstance.placedIconIndex) {
- const PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
+ if (!hasIconTextFit || !placeText || variableTextAnchors.empty()) {
+ shift = {0.0f, 0.0f};
+ }
+
+ 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);
+ };
- auto placedIcon = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {},
- 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) {
@@ -385,7 +400,7 @@ void Placement::placeBucket(
}
if (placeText) {
- if (placedVertical.first && symbolInstance.verticalTextCollisionFeature) {
+ 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);
@@ -393,16 +408,21 @@ void Placement::placeBucket(
}
if (placeIcon) {
- collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, 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);
+ }
}
- if (hasCollisionCircleData) {
- if (symbolInstance.iconCollisionFeature.alongLine && !iconBoxes.empty()) {
- collisionCircles[&symbolInstance.iconCollisionFeature] = iconBoxes;
- }
- if (symbolInstance.textCollisionFeature.alongLine && !textBoxes.empty()) {
- collisionCircles[&symbolInstance.textCollisionFeature] = textBoxes;
- }
+ 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);
@@ -438,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) {
@@ -504,13 +522,13 @@ 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
@@ -518,16 +536,26 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
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) {
@@ -538,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;
@@ -548,8 +576,10 @@ 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;
const bool skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation;
if (!symbol.hidden && symbol.crossTileID != 0u && !skipOrientation) {
@@ -578,7 +608,7 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
(*variableOffset).anchor,
(*variableOffset).width,
(*variableOffset).height,
- (*variableOffset).radialOffset,
+ (*variableOffset).offset,
(*variableOffset).textBoxScale,
renderTextSize);
@@ -598,24 +628,62 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
projectedAnchor.first.y + shift.y);
}
- for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
+ 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 {
+ 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);
+ }
+
result = true;
} else if (bucket.allowVerticalPlacement && bucket.hasTextData()) {
- bucket.text.dynamicVertices.clear();
- for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
- if (symbol.hidden || !symbol.placedOrientation) {
- hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices);
- } else {
- for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
- addDynamicAttributes(symbol.anchorPoint, symbol.angle, bucket.text.dynamicVertices);
+ 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;
}
@@ -625,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);
@@ -656,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) {
@@ -701,27 +769,34 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
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);
@@ -734,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());
@@ -747,10 +822,11 @@ 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;
}
@@ -758,26 +834,36 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
if (circles != collisionCircles.end()) {
for (const auto& circle : circles->second) {
const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !circle.isCircle(), {});
- bucket.collisionCircle->dynamicVertices.extend(4, dynamicVertex);
+ 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*/, {});
- bucket.collisionCircle->dynamicVertices.extend(4 * feature.boxes.size(), dynamicVertex);
+ isText ? bucket.textCollisionCircle->dynamicVertices.extend(4 * feature.boxes.size(), dynamicVertex):
+ bucket.iconCollisionCircle->dynamicVertices.extend(4 * feature.boxes.size(), dynamicVertex);
}
};
-
- if (bucket.hasCollisionBoxData()) {
- // TODO: update collision box opacity based on selected text variant (horizontal | vertical).
- updateCollisionTextBox(symbolInstance.textCollisionFeature, opacityState.text.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) {
- updateCollisionTextBox(*symbolInstance.verticalTextCollisionFeature, opacityState.text.placed);
+ verticalTextShift = updateTextCollisionBox(*symbolInstance.verticalTextCollisionFeature, opacityState.text.placed);
}
- updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
}
- if (bucket.hasCollisionCircleData()) {
- updateCollisionCircles(symbolInstance.textCollisionFeature, opacityState.text.placed);
- updateCollisionCircles(symbolInstance.iconCollisionFeature, opacityState.icon.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.hasTextCollisionCircleData()) {
+ updateCollisionCircles(symbolInstance.textCollisionFeature, opacityState.text.placed, true);
}
}
@@ -850,17 +936,45 @@ void Placement::markUsedOrientation(SymbolBucket& bucket, style::TextWritingMode
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);
@@ -869,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 722a4a0926..b5405cbcd7 100644
--- a/src/mbgl/text/placement.hpp
+++ b/src/mbgl/text/placement.hpp
@@ -34,7 +34,7 @@ public:
class VariableOffset {
public:
- float radialOffset;
+ std::array<float, 2> offset;
float width;
float height;
style::TextVariableAnchorType anchor;
@@ -102,15 +102,15 @@ 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;
@@ -125,6 +125,8 @@ private:
void updateBucketOpacities(SymbolBucket&, const TransformState&, std::set<uint32_t>&);
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;
@@ -133,6 +135,8 @@ private:
TimePoint fadeStartTime;
TimePoint commitTime;
+ float placementZoom;
+ float prevZoomAdjustment = 0;
std::unordered_map<uint32_t, JointPlacement> placements;
std::unordered_map<uint32_t, JointOpacityState> opacities;
@@ -144,6 +148,7 @@ private:
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;
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index b08c2bc0ba..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,7 +54,7 @@ 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,
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index 1ec68189af..145fd2b153 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -44,9 +44,7 @@ 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,
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 7bf0e14f80..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,
@@ -380,7 +413,7 @@ 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,
@@ -399,7 +432,7 @@ const Shaping getShaping(const TaggedString& formattedString,
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, allowVerticalPlacement);
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index f3a01e3caf..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,7 +73,7 @@ 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,
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/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 cd21a027a2..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, false, {}};
+ 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/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 3f6db527d4..24234b0624 100644
--- a/test/storage/offline_database.test.cpp
+++ b/test/storage/offline_database.test.cpp
@@ -1093,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);
@@ -1103,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());
}
@@ -1131,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());
@@ -1488,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());
@@ -1566,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));
@@ -1586,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));
@@ -1601,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/test-files.json b/test/test-files.json
index e46f833269..b239ac5ad8 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",
diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp
index ccf83e81c8..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,
+ shaping, nullopt, nullopt, layout_, placementType,
textOffset, positions, false);
- return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, 0.0f, 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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, false, {} };
+ 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 17c2fc96af..b22cd7da36 100644
--- a/test/text/shaping.test.cpp
+++ b/test/text/shaping.test.cpp
@@ -33,7 +33,7 @@ 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,
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/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.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/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/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/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/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
-)