summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLangston Smith <langston.smith@mapbox.com>2017-10-10 17:04:11 -0700
committerLangston Smith <langston.smith@mapbox.com>2017-10-10 17:04:11 -0700
commita829c2de392b8f826e939f1a5061c111630f48e0 (patch)
tree7b0c7e2a033c3ad5489648568eb8f2fcbfcac74f
parent4d56366824b51095911dc591c664b963bc04634f (diff)
parent98a47884f06a8f165a2c15a54f82b356c8ef23d8 (diff)
downloadqtlocation-mapboxgl-a829c2de392b8f826e939f1a5061c111630f48e0.tar.gz
Merge branch 'master' into ls-readme-updates
-rw-r--r--.gitignore4
-rw-r--r--.tx/config7
-rw-r--r--CMakeLists.txt21
-rw-r--r--DEVELOPING.md11
-rw-r--r--Makefile19
-rw-r--r--README.md2
-rw-r--r--benchmark/function/camera_function.benchmark.cpp69
-rw-r--r--benchmark/function/composite_function.benchmark.cpp76
-rw-r--r--benchmark/function/source_function.benchmark.cpp70
-rw-r--r--benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp41
-rw-r--r--circle.yml52
-rw-r--r--cloudformation/travis.template34
-rw-r--r--cmake/benchmark-files.cmake6
-rw-r--r--cmake/benchmark.cmake2
-rw-r--r--cmake/core-files.cmake14
-rw-r--r--cmake/core.cmake2
-rw-r--r--cmake/filesource.cmake57
-rw-r--r--cmake/glfw.cmake2
-rw-r--r--cmake/mbgl.cmake36
-rw-r--r--cmake/node.cmake4
-rw-r--r--cmake/render.cmake2
-rw-r--r--cmake/test-files.cmake2
-rw-r--r--cmake/test.cmake2
-rw-r--r--include/mbgl/actor/actor.hpp11
-rw-r--r--include/mbgl/actor/message.hpp26
-rw-r--r--include/mbgl/actor/scheduler.hpp15
-rw-r--r--include/mbgl/map/map.hpp11
-rw-r--r--include/mbgl/math/log2.hpp4
-rw-r--r--include/mbgl/renderer/renderer.hpp2
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp2
-rw-r--r--include/mbgl/storage/default_file_source.hpp1
-rw-r--r--include/mbgl/storage/offline.hpp4
-rw-r--r--include/mbgl/storage/online_file_source.hpp3
-rw-r--r--include/mbgl/storage/resource.hpp1
-rw-r--r--include/mbgl/storage/response.hpp11
-rw-r--r--include/mbgl/style/conversion/geojson_options.hpp10
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp7
-rw-r--r--include/mbgl/style/layers/custom_layer.hpp16
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp22
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp1
-rw-r--r--include/mbgl/style/style.hpp6
-rw-r--r--include/mbgl/style/types.hpp2
-rw-r--r--include/mbgl/tile/tile_id.hpp (renamed from src/mbgl/tile/tile_id.hpp)33
-rw-r--r--include/mbgl/util/constants.hpp5
-rw-r--r--include/mbgl/util/geometry.hpp6
-rw-r--r--include/mbgl/util/image.hpp26
-rw-r--r--include/mbgl/util/projection.hpp22
-rw-r--r--include/mbgl/util/run_loop.hpp12
-rw-r--r--include/mbgl/util/thread.hpp (renamed from src/mbgl/util/thread.hpp)2
-rw-r--r--include/mbgl/util/tileset.hpp3
m---------mapbox-gl-js0
-rw-r--r--package.json13
-rw-r--r--platform/android/CHANGELOG.md62
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle12
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties3
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml37
-rw-r--r--platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml587
-rw-r--r--platform/android/MapboxGLAndroidSDK/proguard-rules.pro7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java293
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java24
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java43
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java33
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java293
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java281
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java36
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java27
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java182
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java28
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java145
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java29
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java63
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java37
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java54
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java260
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java56
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java35
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java27
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpgbin166053 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.pngbin928 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml52
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/build.gradle8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml166
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml2468
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java42
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java146
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java63
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java54
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java371
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java43
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml58
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java188
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java174
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java137
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java192
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java252
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java131
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java98
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java216
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java196
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java74
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml28
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml121
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml25
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml33
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml)8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml30
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson2741
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml96
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml65
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml185
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml65
-rw-r--r--platform/android/README.md11
-rw-r--r--platform/android/build.gradle1
-rw-r--r--platform/android/config.cmake70
-rw-r--r--platform/android/dependencies.gradle19
-rw-r--r--platform/android/scripts/generate-style-code.js6
-rwxr-xr-xplatform/android/src/android_renderer_backend.cpp63
-rwxr-xr-xplatform/android/src/android_renderer_backend.hpp41
-rw-r--r--platform/android/src/android_renderer_frontend.cpp109
-rw-r--r--platform/android/src/android_renderer_frontend.hpp20
-rw-r--r--platform/android/src/example_custom_layer.cpp8
-rw-r--r--platform/android/src/file_source.cpp4
-rw-r--r--platform/android/src/http_file_source.cpp4
-rwxr-xr-xplatform/android/src/jni.cpp8
-rw-r--r--platform/android/src/jni/generic_global_ref_deleter.hpp29
-rw-r--r--platform/android/src/map/camera_position.cpp18
-rw-r--r--platform/android/src/map/camera_position.hpp2
-rw-r--r--platform/android/src/map_renderer.cpp200
-rw-r--r--platform/android/src/map_renderer.hpp126
-rw-r--r--platform/android/src/map_renderer_runnable.cpp49
-rw-r--r--platform/android/src/map_renderer_runnable.hpp50
-rwxr-xr-xplatform/android/src/native_map_view.cpp618
-rwxr-xr-xplatform/android/src/native_map_view.hpp89
-rw-r--r--platform/android/src/run_loop.cpp11
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.cpp110
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.hpp61
-rw-r--r--platform/android/src/style/conversion/types.hpp8
-rw-r--r--platform/android/src/style/conversion/types_string_values.hpp56
-rw-r--r--platform/android/src/style/layers/custom_layer.cpp5
-rw-r--r--platform/android/src/style/layers/custom_layer.hpp2
-rw-r--r--platform/android/src/style/layers/symbol_layer.cpp7
-rw-r--r--platform/android/src/style/layers/symbol_layer.hpp2
-rw-r--r--platform/android/tests/docs/UI_TESTS.md2
-rw-r--r--platform/android/tests/docs/UNIT_TESTS.md2
-rw-r--r--platform/darwin/docs/guides/Working with GeoJSON Data.md6
-rw-r--r--platform/darwin/resources/ca.lproj/Foundation.strings4
-rw-r--r--platform/darwin/resources/de.lproj/Foundation.stringsdict16
-rw-r--r--platform/darwin/resources/fr.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/lt.lproj/Foundation.stringsdict6
-rw-r--r--platform/darwin/resources/ru.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/uk.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/uk.lproj/Foundation.stringsdict16
-rw-r--r--platform/darwin/resources/vi.lproj/Foundation.stringsdict48
-rw-r--r--platform/darwin/resources/zh-Hant.lproj/Foundation.strings42
-rw-r--r--platform/darwin/scripts/generate-style-code.js14
-rw-r--r--platform/darwin/src/MGLFeature.h14
-rw-r--r--platform/darwin/src/MGLFeature.mm49
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h148
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm189
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm3
-rw-r--r--platform/darwin/src/MGLPolygon.mm10
-rw-r--r--platform/darwin/src/MGLPolyline.h14
-rw-r--r--platform/darwin/src/MGLPolyline.mm10
-rw-r--r--platform/darwin/src/MGLShapeSource.mm4
-rw-r--r--platform/darwin/src/MGLSource.mm27
-rw-r--r--platform/darwin/src/MGLSource_Private.h25
-rw-r--r--platform/darwin/src/MGLStyle.h61
-rw-r--r--platform/darwin/src/MGLStyle.mm182
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h104
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm57
-rw-r--r--platform/darwin/src/MGLTypes.h2
-rw-r--r--platform/darwin/src/MGLVectorSource+MGLAdditions.h (renamed from platform/macos/app/MGLVectorSource+MBXAdditions.h)2
-rw-r--r--platform/darwin/src/MGLVectorSource+MGLAdditions.m (renamed from platform/macos/app/MGLVectorSource+MBXAdditions.m)4
-rw-r--r--platform/darwin/src/MGLVectorSource.mm4
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.m11
-rw-r--r--platform/darwin/src/http_file_source.mm4
-rw-r--r--platform/darwin/src/run_loop.cpp20
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift12
-rw-r--r--platform/darwin/test/MGLNSStringAdditionsTests.m2
-rw-r--r--platform/darwin/test/MGLSDKTestHelpers.swift3
-rw-r--r--platform/darwin/test/MGLStyleTests.mm5
-rw-r--r--platform/darwin/test/MGLStyleValueTests.swift97
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm110
-rw-r--r--platform/default/default_file_source.cpp71
-rw-r--r--platform/default/file_source_request.cpp (renamed from src/mbgl/storage/file_source_request.cpp)4
-rw-r--r--platform/default/http_file_source.cpp4
-rw-r--r--platform/default/mbgl/gl/headless_backend.cpp10
-rw-r--r--platform/default/mbgl/gl/headless_backend.hpp1
-rw-r--r--platform/default/mbgl/gl/headless_frontend.cpp8
-rw-r--r--platform/default/mbgl/gl/headless_frontend.hpp5
-rw-r--r--platform/default/mbgl/map/map_snapshotter.cpp76
-rw-r--r--platform/default/mbgl/map/map_snapshotter.hpp40
-rw-r--r--platform/default/mbgl/storage/file_source_request.hpp (renamed from src/mbgl/storage/file_source_request.hpp)0
-rw-r--r--platform/default/mbgl/storage/offline.cpp33
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp193
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp1
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp4
-rw-r--r--platform/default/mbgl/storage/offline_schema.cpp.include2
-rw-r--r--platform/default/mbgl/storage/offline_schema.sql2
-rw-r--r--platform/default/online_file_source.cpp41
-rw-r--r--platform/default/run_loop.cpp13
-rw-r--r--platform/default/sqlite3.cpp5
-rw-r--r--platform/default/thread_local.cpp2
-rw-r--r--platform/glfw/glfw_view.cpp4
-rw-r--r--platform/glfw/glfw_view.hpp7
-rw-r--r--platform/glfw/main.cpp64
-rw-r--r--platform/glfw/settings_json.cpp3
-rw-r--r--platform/glfw/settings_json.hpp1
-rw-r--r--platform/ios/CHANGELOG.md62
-rw-r--r--platform/ios/DEVELOPING.md7
-rw-r--r--platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK-symbols.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK.podspec2
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json5
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/Contents.json15
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings.pdfbin0 -> 9177 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings.pngbin528 -> 0 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.pngbin1130 -> 0 bytes
-rw-r--r--platform/ios/app/Default-568h@2x.pngbin18594 -> 0 bytes
-rw-r--r--platform/ios/app/Info.plist17
-rw-r--r--platform/ios/app/LaunchScreen.storyboard14
-rw-r--r--platform/ios/app/MBXAppDelegate.m18
-rw-r--r--platform/ios/app/MBXSnapshotsViewController.h5
-rw-r--r--platform/ios/app/MBXSnapshotsViewController.m66
-rw-r--r--platform/ios/app/MBXViewController.m271
-rw-r--r--platform/ios/app/Main.storyboard110
-rw-r--r--platform/ios/bitrise.yml2
-rw-r--r--platform/ios/config.cmake68
-rw-r--r--platform/ios/docs/doc-README.md2
-rw-r--r--platform/ios/docs/pod-README.md2
-rw-r--r--platform/ios/framework/Settings.bundle/hu.lproj/Root.strings3
-rw-r--r--platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings4
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj155
-rw-r--r--platform/ios/resources/es.lproj/Localizable.strings22
-rw-r--r--platform/ios/resources/fr.lproj/Localizable.strings18
-rw-r--r--platform/ios/resources/lt.lproj/Localizable.stringsdict12
-rw-r--r--platform/ios/resources/ru.lproj/Localizable.strings20
-rw-r--r--platform/ios/resources/sv.lproj/Localizable.strings6
-rw-r--r--platform/ios/resources/uk.lproj/Localizable.strings26
-rw-r--r--platform/ios/resources/uk.lproj/Localizable.stringsdict38
-rw-r--r--platform/ios/resources/zh-Hant.lproj/Localizable.strings39
-rw-r--r--platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict30
-rwxr-xr-xplatform/ios/scripts/package.sh40
-rw-r--r--platform/ios/src/MGLFaux3DUserLocationAnnotationView.h10
-rw-r--r--platform/ios/src/MGLFaux3DUserLocationAnnotationView.m181
-rw-r--r--platform/ios/src/MGLMapView+IBAdditions.h1
-rw-r--r--platform/ios/src/MGLMapView.h104
-rw-r--r--platform/ios/src/MGLMapView.mm613
-rw-r--r--platform/ios/src/MGLUserLocation.h3
-rw-r--r--platform/ios/src/MGLUserLocation.m3
-rw-r--r--platform/ios/src/MGLUserLocationHeadingArrowLayer.h11
-rw-r--r--platform/ios/src/MGLUserLocationHeadingArrowLayer.m59
-rw-r--r--platform/ios/src/MGLUserLocationHeadingBeamLayer.h11
-rw-r--r--platform/ios/src/MGLUserLocationHeadingBeamLayer.m104
-rw-r--r--platform/ios/src/MGLUserLocationHeadingIndicator.h10
-rw-r--r--platform/ios/src/Mapbox.h1
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.h2
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.mm13
m---------platform/ios/uitest/KIF0
m---------platform/ios/uitest/OHHTTPStubs0
-rw-r--r--platform/linux/config.cmake54
-rw-r--r--platform/linux/src/headless_display_egl.cpp3
-rw-r--r--platform/macos/CHANGELOG.md40
-rw-r--r--platform/macos/Mapbox-macOS-SDK-symbols.podspec2
-rw-r--r--platform/macos/Mapbox-macOS-SDK.podspec2
-rw-r--r--platform/macos/app/Base.lproj/MainMenu.xib16
-rw-r--r--platform/macos/app/Base.lproj/MapDocument.xib6
-rw-r--r--platform/macos/app/MapDocument.m124
-rw-r--r--platform/macos/app/hu.lproj/Localizable.strings0
-rw-r--r--platform/macos/config.cmake53
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj75
-rw-r--r--platform/macos/sdk/ca.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/es.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/fr.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/hu.lproj/Localizable.strings27
-rw-r--r--platform/macos/sdk/ru.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/uk.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/vi.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/zh-Hant.lproj/Localizable.strings12
-rw-r--r--platform/macos/src/MGLMapView.h40
-rw-r--r--platform/macos/src/MGLMapView.mm26
-rw-r--r--platform/macos/src/Mapbox.h1
-rw-r--r--platform/node/CHANGELOG.md15
-rwxr-xr-xplatform/node/scripts/after_success.sh4
-rw-r--r--platform/node/src/node_geojson.hpp8
-rw-r--r--platform/node/src/node_map.cpp84
-rw-r--r--platform/node/src/node_map.hpp1
-rw-r--r--platform/node/test/ignores.json56
-rw-r--r--platform/node/test/js/map.test.js1
-rw-r--r--platform/node/test/render.test.js8
-rw-r--r--platform/node/test/suite_implementation.js64
-rw-r--r--platform/qt/config.cmake28
-rw-r--r--platform/qt/include/qmapboxgl.hpp6
-rw-r--r--platform/qt/qt.cmake75
-rw-r--r--platform/qt/qt4.cmake25
-rw-r--r--platform/qt/qt5.cmake28
-rw-r--r--platform/qt/src/headless_backend_qt.cpp (renamed from platform/qt/test/headless_backend_qt.cpp)0
-rw-r--r--platform/qt/src/http_request.cpp4
-rw-r--r--platform/qt/src/qmapboxgl.cpp47
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp9
-rw-r--r--platform/qt/src/run_loop.cpp16
-rw-r--r--platform/qt/src/sqlite3.cpp1
-rw-r--r--platform/qt/src/thread_local.cpp4
-rw-r--r--platform/qt/test/qmapboxgl.cpp99
-rw-r--r--platform/qt/test/qmapboxgl.test.cpp77
-rw-r--r--platform/qt/test/qmapboxgl.test.hpp36
-rw-r--r--scripts/config.xcconfig.in4
-rwxr-xr-xscripts/generate-shaders.js92
-rw-r--r--scripts/generate-style-code.js3
-rw-r--r--src/mbgl/actor/scheduler.cpp19
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp38
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp11
-rw-r--r--src/mbgl/annotation/render_annotation_source.cpp6
-rw-r--r--src/mbgl/annotation/render_annotation_source.hpp2
-rw-r--r--src/mbgl/geometry/feature_index.cpp97
-rw-r--r--src/mbgl/geometry/feature_index.hpp9
-rw-r--r--src/mbgl/gl/context.cpp21
-rw-r--r--src/mbgl/gl/context.hpp8
-rw-r--r--src/mbgl/gl/renderbuffer.hpp16
-rw-r--r--src/mbgl/gl/value.cpp6
-rw-r--r--src/mbgl/gl/value.hpp2
-rw-r--r--src/mbgl/layout/symbol_layout.cpp5
-rw-r--r--src/mbgl/layout/symbol_projection.cpp72
-rw-r--r--src/mbgl/map/map.cpp138
-rw-r--r--src/mbgl/map/transform_state.cpp2
-rw-r--r--src/mbgl/map/transform_state.hpp4
-rw-r--r--src/mbgl/map/update.hpp26
-rw-r--r--src/mbgl/map/zoom_history.hpp37
-rw-r--r--src/mbgl/programs/attributes.hpp1
-rw-r--r--src/mbgl/programs/line_program.cpp2
-rw-r--r--src/mbgl/programs/line_program.hpp13
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.cpp6
-rw-r--r--src/mbgl/renderer/buckets/raster_bucket.cpp13
-rw-r--r--src/mbgl/renderer/image_manager.cpp21
-rw-r--r--src/mbgl/renderer/image_manager.hpp8
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp8
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp5
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp194
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp4
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp54
-rw-r--r--src/mbgl/renderer/paint_parameters.cpp17
-rw-r--r--src/mbgl/renderer/paint_parameters.hpp8
-rw-r--r--src/mbgl/renderer/render_item.hpp36
-rw-r--r--src/mbgl/renderer/render_pass.hpp1
-rw-r--r--src/mbgl/renderer/render_source.hpp3
-rw-r--r--src/mbgl/renderer/render_static_data.cpp6
-rw-r--r--src/mbgl/renderer/render_static_data.hpp5
-rw-r--r--src/mbgl/renderer/render_style.cpp449
-rw-r--r--src/mbgl/renderer/render_style.hpp92
-rw-r--r--src/mbgl/renderer/render_style_observer.hpp14
-rw-r--r--src/mbgl/renderer/render_tile.cpp86
-rw-r--r--src/mbgl/renderer/renderer.cpp12
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp579
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp70
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp24
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_image_source.cpp9
-rw-r--r--src/mbgl/renderer/sources/render_image_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.cpp2
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.hpp2
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.cpp4
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.hpp2
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp45
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp6
-rw-r--r--src/mbgl/renderer/update_parameters.hpp4
-rw-r--r--src/mbgl/shaders/circle.cpp32
-rw-r--r--src/mbgl/shaders/debug.cpp7
-rw-r--r--src/mbgl/shaders/fill.cpp12
-rw-r--r--src/mbgl/shaders/fill_extrusion.cpp16
-rw-r--r--src/mbgl/shaders/fill_extrusion_pattern.cpp12
-rw-r--r--src/mbgl/shaders/fill_outline.cpp12
-rw-r--r--src/mbgl/shaders/fill_outline_pattern.cpp8
-rw-r--r--src/mbgl/shaders/fill_pattern.cpp8
-rw-r--r--src/mbgl/shaders/line.cpp43
-rw-r--r--src/mbgl/shaders/line_pattern.cpp38
-rw-r--r--src/mbgl/shaders/line_sdf.cpp48
-rw-r--r--src/mbgl/shaders/raster.cpp7
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp8
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp24
-rw-r--r--src/mbgl/sprite/sprite_loader.cpp4
-rw-r--r--src/mbgl/storage/response.cpp1
-rw-r--r--src/mbgl/style/function/categorical_stops.cpp2
-rw-r--r--src/mbgl/style/function/identity_stops.cpp4
-rw-r--r--src/mbgl/style/image_impl.hpp1
-rw-r--r--src/mbgl/style/layers/custom_layer.cpp11
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp2
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp2
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp34
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp14
-rw-r--r--src/mbgl/style/observer.hpp3
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.cpp2
-rw-r--r--src/mbgl/style/style.cpp16
-rw-r--r--src/mbgl/style/style_impl.cpp42
-rw-r--r--src/mbgl/style/style_impl.hpp12
-rw-r--r--src/mbgl/style/types.cpp20
-rw-r--r--src/mbgl/text/glyph.hpp6
-rw-r--r--src/mbgl/text/glyph_manager.cpp11
-rw-r--r--src/mbgl/text/glyph_manager.hpp2
-rw-r--r--src/mbgl/text/placement_config.hpp6
-rw-r--r--src/mbgl/text/quads.cpp23
-rw-r--r--src/mbgl/text/shaping.cpp110
-rw-r--r--src/mbgl/text/shaping.hpp7
-rw-r--r--src/mbgl/tile/geojson_tile.cpp96
-rw-r--r--src/mbgl/tile/geojson_tile_data.hpp94
-rw-r--r--src/mbgl/tile/geometry_tile.cpp65
-rw-r--r--src/mbgl/tile/geometry_tile.hpp31
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp23
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp3
-rw-r--r--src/mbgl/tile/raster_tile.cpp22
-rw-r--r--src/mbgl/tile/raster_tile.hpp6
-rw-r--r--src/mbgl/tile/raster_tile_worker.cpp8
-rw-r--r--src/mbgl/tile/raster_tile_worker.hpp2
-rw-r--r--src/mbgl/tile/tile.cpp2
-rw-r--r--src/mbgl/tile/tile.hpp4
-rw-r--r--src/mbgl/tile/tile_id_hash.cpp29
-rw-r--r--src/mbgl/tile/tile_loader_impl.hpp3
-rw-r--r--src/mbgl/util/constants.cpp2
-rw-r--r--src/mbgl/util/http_timeout.cpp2
-rw-r--r--src/mbgl/util/math.hpp6
-rw-r--r--src/mbgl/util/offscreen_texture.cpp32
-rw-r--r--src/mbgl/util/offscreen_texture.hpp11
-rw-r--r--src/mbgl/util/tile_cover.cpp21
-rw-r--r--src/mbgl/util/tile_cover.hpp3
-rw-r--r--test/actor/actor.test.cpp40
-rw-r--r--test/actor/actor_ref.test.cpp22
-rw-r--r--test/api/recycle_map.cpp58
-rw-r--r--test/api/zoom_history.cpp71
-rw-r--r--test/fixtures/api/empty-zoomed.json6
-rw-r--r--test/fixtures/map/nocontent/expected.pngbin0 -> 1685 bytes
-rw-r--r--test/fixtures/offline_database/v5.dbbin0 -> 19456 bytes
-rw-r--r--test/fixtures/offline_database/v999.dbbin0 -> 19456 bytes
-rw-r--r--test/fixtures/recycle_map/default_marker/expected.pngbin0 -> 1043 bytes
-rw-r--r--test/fixtures/recycle_map/flipped_marker/expected.pngbin0 -> 1031 bytes
-rw-r--r--test/fixtures/style_parser/geojson-missing-properties.info.json6
-rw-r--r--test/fixtures/style_parser/geojson-missing-properties.style.json9
-rw-r--r--test/fixtures/zoom_history/expected.pngbin0 -> 3894 bytes
-rw-r--r--test/gl/bucket.test.cpp62
-rw-r--r--test/map/map.test.cpp83
-rw-r--r--test/renderer/backend_scope.test.cpp12
-rw-r--r--test/renderer/image_manager.test.cpp11
-rw-r--r--test/src/mbgl/test/stub_file_source.cpp16
-rw-r--r--test/src/mbgl/test/stub_file_source.hpp8
-rw-r--r--test/storage/default_file_source.test.cpp188
-rw-r--r--test/storage/http_file_source.test.cpp9
-rw-r--r--test/storage/offline.test.cpp8
-rw-r--r--test/storage/offline_database.test.cpp93
-rw-r--r--test/storage/online_file_source.test.cpp10
-rwxr-xr-xtest/storage/server.js6
-rw-r--r--test/style/conversion/geojson_options.test.cpp2
-rw-r--r--test/style/style.test.cpp18
-rw-r--r--test/text/glyph_loader.test.cpp49
-rw-r--r--test/text/quads.test.cpp6
-rw-r--r--test/tile/annotation_tile.test.cpp13
-rw-r--r--test/tile/geojson_tile.test.cpp38
-rw-r--r--test/tile/raster_tile.test.cpp20
-rw-r--r--test/tile/vector_tile.test.cpp9
-rw-r--r--test/util/tile_cover.test.cpp9
582 files changed, 16458 insertions, 9155 deletions
diff --git a/.gitignore b/.gitignore
index b6a8498460..ce65e4c9b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,8 +19,8 @@ xcuserdata
/test/fixtures/offline_database/offline.db-*
/test/fixtures/offline_database/invalid.db
/test/fixtures/offline_database/invalid.db-*
-/test/fixtures/offline_database/v*.db
-/test/fixtures/offline_database/v*.db-*
+/test/fixtures/offline_database/migrated.db
+/test/fixtures/offline_database/migrated.db-*
/test/fixtures/**/actual.png
/test/fixtures/**/diff.png
/test/output
diff --git a/.tx/config b/.tx/config
index b464acb01a..1de8f78303 100644
--- a/.tx/config
+++ b/.tx/config
@@ -9,6 +9,13 @@ source_file = platform/darwin/resources/Base.lproj/Foundation.strings
source_lang = en
type = STRINGS
+[mapbox-gl-native.foundationstringsdict-darwin]
+file_filter = platform/darwin/resources/<lang>.lproj/Foundation.stringsdict
+lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant
+source_file = platform/darwin/resources/en.lproj/Foundation.stringsdict
+source_lang = en
+type = STRINGSDICT
+
[mapbox-gl-native.localizablestrings-ios]
file_filter = platform/ios/resources/<lang>.lproj/Localizable.strings
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8ce43f998a..a86bbd192c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,18 +48,18 @@ mason_use(any VERSION 8fef1e9 HEADER_ONLY)
mason_use(unique_resource VERSION cba309e HEADER_ONLY)
mason_use(rapidjson VERSION 1.1.0 HEADER_ONLY)
mason_use(boost VERSION 1.62.0 HEADER_ONLY)
-mason_use(geojsonvt VERSION 6.2.0 HEADER_ONLY)
-mason_use(supercluster VERSION 0.2.1 HEADER_ONLY)
+mason_use(geojsonvt VERSION 6.3.0 HEADER_ONLY)
+mason_use(supercluster VERSION 0.2.2 HEADER_ONLY)
mason_use(kdbush VERSION 0.1.1-1 HEADER_ONLY)
mason_use(earcut VERSION 0.12.3 HEADER_ONLY)
mason_use(protozero VERSION 1.5.2 HEADER_ONLY)
mason_use(pixelmatch VERSION 0.10.0 HEADER_ONLY)
-mason_use(geojson VERSION 0.4.0 HEADER_ONLY)
-mason_use(polylabel VERSION 1.0.2 HEADER_ONLY)
-mason_use(wagyu VERSION 0.4.1 HEADER_ONLY)
-mason_use(shelf-pack VERSION 2.1.0 HEADER_ONLY)
+mason_use(geojson VERSION 0.4.2 HEADER_ONLY)
+mason_use(polylabel VERSION 1.0.3 HEADER_ONLY)
+mason_use(wagyu VERSION 0.4.3 HEADER_ONLY)
+mason_use(shelf-pack VERSION 2.1.1 HEADER_ONLY)
mason_use(cheap-ruler VERSION 2.5.3 HEADER_ONLY)
-mason_use(vector-tile VERSION 1.0.0-rc6 HEADER_ONLY)
+mason_use(vector-tile VERSION 1.0.0-rc7 HEADER_ONLY)
add_definitions(-DRAPIDJSON_HAS_STDSTRING=1)
@@ -127,10 +127,12 @@ if(NOT EXISTS ${CMAKE_SOURCE_DIR}/platform/${MBGL_PLATFORM}/config.cmake)
message(ERROR "Can't find config.cmake file for platform ${MBGL_PLATFORM}")
endif()
-include(cmake/loop-uv.cmake)
-
include(platform/${MBGL_PLATFORM}/config.cmake)
+if (COMMAND mbgl_filesource)
+ include(cmake/filesource.cmake)
+endif()
+
include(cmake/core-files.cmake)
include(cmake/core.cmake)
@@ -163,5 +165,6 @@ endif()
if(CMAKE_GENERATOR STREQUAL "Xcode")
write_xcconfig_target_properties(
mbgl-core
+ mbgl-filesource
)
endif()
diff --git a/DEVELOPING.md b/DEVELOPING.md
index c5c5c53f8a..f70d1f94ea 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -110,3 +110,14 @@ constexpr bool test2(int i) {
- Either remove `constexpr` specifier or replace it with `inline` in case of
functions
+
+
+## [Polymorphic lambdas](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68278)
+
+GCC 5.2.0 crashes with polymorphic lambdas and this version of the compiler
+is currently used in Qt Automotive. Luckily polymorphic lambdas are rarely
+used/needed but we had one incident fixed by #9665.
+
+### Workarounds
+
+- Copy & Paste™ the code.
diff --git a/Makefile b/Makefile
index 47821f9447..b4863a9b9e 100644
--- a/Makefile
+++ b/Makefile
@@ -108,7 +108,7 @@ run-test-%: test
run-benchmark: run-benchmark-.
run-benchmark-%: benchmark
- $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/mbgl-benchmark --benchmark_filter=$*
+ $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/mbgl-benchmark --benchmark_filter=$* ${BENCHMARK_ARGS}
.PHONY: node-benchmark
node-benchmark: $(MACOS_PROJ_PATH)
@@ -440,6 +440,12 @@ test-node: node
npm test
npm run test-suite
+.PHONY: test-node-recycle-map
+test-node-recycle-map: node
+ npm test
+ npm run test-render -- --recycle-map --shuffle
+ npm run test-query
+
#### Android targets ###########################################################
MBGL_ANDROID_ABIS = arm-v5;armeabi
@@ -528,7 +534,7 @@ run-android-core-test-$1: run-android-core-test-$1-*
# Run the test app on connected android device with specified abi
.PHONY: run-android-$1
run-android-$1: platform/android/configuration.gradle
- adb uninstall com.mapbox.mapboxsdk.testapp > /dev/null
+ -adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:install$(BUILDTYPE) && adb shell am start -n com.mapbox.mapboxsdk.testapp/.activity.FeatureOverviewActivity
# Build test app instrumentation tests apk and test app apk for specified abi
@@ -539,12 +545,12 @@ android-ui-test-$1: platform/android/configuration.gradle
# Run test app instrumentation tests on a connected android device or emulator with specified abi
.PHONY: run-android-ui-test-$1
run-android-ui-test-$1: platform/android/configuration.gradle
- adb uninstall com.mapbox.mapboxsdk.testapp > /dev/null
+ -adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest
# Run Java Instrumentation tests on a connected android device or emulator with specified abi and test filter
run-android-ui-test-$1-%: platform/android/configuration.gradle
- adb uninstall com.mapbox.mapboxsdk.testapp > /dev/null
+ -adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="$$*"
endef
@@ -588,6 +594,7 @@ run-android-ui-test-aws: platform/android/configuration.gradle
# Builds a release package of the Android SDK
.PHONY: apackage
apackage: platform/android/configuration.gradle
+ make android-lib-arm-v5 && make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 && make android-lib-mips
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all assemble$(BUILDTYPE)
# Uploads the compiled Android SDK to Maven
@@ -610,6 +617,10 @@ run-android-ui-test-spoon: platform/android/configuration.gradle
test-code-android:
node platform/android/scripts/generate-test-code.js
+# Runs checkstyle and lint on the Android code
+.PHONY: android-check
+android-check : android-checkstyle android-lint-sdk android-lint-test-app
+
# Runs checkstyle on the Android code
.PHONY: android-checkstyle
android-checkstyle: platform/android/configuration.gradle
diff --git a/README.md b/README.md
index 06de2371b0..8f6ea99704 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ Additional Mapbox GL Native–based libraries for **hybrid applications** are de
| Toolkit | Android | iOS | Developer |
| ---------------------------------------- | --------|-----|------------ |
-| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | |
+| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox |
| [Apache Cordova](http://plugins.telerik.com/cordova/plugin/mapbox/) ([npm](https://www.npmjs.com/package/cordova-plugin-mapbox)) | :white_check_mark: | :white_check_mark: | Telerik |
| [NativeScript](http://plugins.telerik.com/nativescript/plugin/mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik |
| [Xamarin](https://components.xamarin.com/view/mapboxsdk/) | :white_check_mark: | :white_check_mark: | Xamarin |
diff --git a/benchmark/function/camera_function.benchmark.cpp b/benchmark/function/camera_function.benchmark.cpp
new file mode 100644
index 0000000000..1f8fe4579f
--- /dev/null
+++ b/benchmark/function/camera_function.benchmark.cpp
@@ -0,0 +1,69 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/style/function/source_function.hpp>
+
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+#include <rapidjson/document.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
+
+ std::string stops = "[";
+ for (size_t i = 0; i < stopCount; i++) {
+ std::string value = std::to_string(24.0f / stopCount * i);
+ if (stops.size() > 1) stops += ",";
+ stops += "[" + value + ", " + value + "]";
+ }
+ stops += "]";
+
+ doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + "}");
+ return doc;
+}
+
+static void Parse_CameraFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+
+ while (state.KeepRunning()) {
+ conversion::Error error;
+ state.PauseTiming();
+ auto doc = createFunctionJSON(stopCount);
+ state.ResumeTiming();
+ optional<CameraFunction<float>> result = conversion::convert<CameraFunction<float>, JSValue>(doc, error);
+ if (!result) {
+ state.SkipWithError(error.message.c_str());
+ }
+ }
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+static void Evaluate_CameraFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+ auto doc = createFunctionJSON(stopCount);
+ conversion::Error error;
+ optional<CameraFunction<float>> function = conversion::convert<CameraFunction<float>, JSValue>(doc, error);
+ if (!function) {
+ state.SkipWithError(error.message.c_str());
+ }
+
+ while(state.KeepRunning()) {
+ float z = 24.0f * static_cast<float>(rand() % 100) / 100;
+ function->evaluate(z);
+ }
+
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+BENCHMARK(Parse_CameraFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+BENCHMARK(Evaluate_CameraFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+
diff --git a/benchmark/function/composite_function.benchmark.cpp b/benchmark/function/composite_function.benchmark.cpp
new file mode 100644
index 0000000000..f04b6c7073
--- /dev/null
+++ b/benchmark/function/composite_function.benchmark.cpp
@@ -0,0 +1,76 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/benchmark/stub_geometry_tile_feature.hpp>
+
+#include <mbgl/style/function/composite_exponential_stops.hpp>
+#include <mbgl/style/function/composite_function.hpp>
+
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+#include <rapidjson/document.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
+
+ std::string stops = "[";
+ for (size_t outerStop = 0; outerStop < stopCount; outerStop++) {
+ for (size_t innerStop = 0; innerStop < stopCount; innerStop++) {
+ std::string zoom = std::to_string(24.0f / stopCount * outerStop);
+ std::string value = std::to_string(100.0f / stopCount * innerStop);
+
+ if (stops.size() > 1) stops += ",";
+ stops += R"([{"zoom":)" + zoom + R"(,"value":)" + value + "}, " + value + "]";
+ }
+ }
+ stops += "]";
+
+ doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})");
+ return doc;
+}
+
+static void Parse_CompositeFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+
+ while (state.KeepRunning()) {
+ conversion::Error error;
+ state.PauseTiming();
+ auto doc = createFunctionJSON(stopCount);
+ state.ResumeTiming();
+ optional<CompositeFunction<float>> result = conversion::convert<style::CompositeFunction<float>, JSValue>(doc, error);
+ if (!result) {
+ state.SkipWithError(error.message.c_str());
+ }
+ }
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+static void Evaluate_CompositeFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+ auto doc = createFunctionJSON(stopCount);
+ conversion::Error error;
+ optional<CompositeFunction<float>> function = conversion::convert<CompositeFunction<float>, JSValue>(doc, error);
+ if (!function) {
+ state.SkipWithError(error.message.c_str());
+ }
+
+ while(state.KeepRunning()) {
+ float z = 24.0f * static_cast<float>(rand() % 100) / 100;
+ function->evaluate(z, StubGeometryTileFeature(PropertyMap { { "x", static_cast<int64_t>(rand() % 100) } }), -1.0f);
+ }
+
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+BENCHMARK(Parse_CompositeFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+BENCHMARK(Evaluate_CompositeFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+
diff --git a/benchmark/function/source_function.benchmark.cpp b/benchmark/function/source_function.benchmark.cpp
new file mode 100644
index 0000000000..14e729eee2
--- /dev/null
+++ b/benchmark/function/source_function.benchmark.cpp
@@ -0,0 +1,70 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/benchmark/stub_geometry_tile_feature.hpp>
+
+#include <mbgl/style/function/source_function.hpp>
+
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+#include <rapidjson/document.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+static rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> createFunctionJSON(size_t stopCount) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
+
+ std::string stops = "[";
+ for (size_t i = 0; i < stopCount; i++) {
+ std::string value = std::to_string(100.0f / stopCount * i);
+ if (stops.size() > 1) stops += ",";
+ stops += "[" + value + ", " + value + "]";
+ }
+ stops += "]";
+
+ doc.Parse<0>(R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})");
+ return doc;
+}
+
+static void Parse_SourceFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+
+ while (state.KeepRunning()) {
+ conversion::Error error;
+ state.PauseTiming();
+ auto doc = createFunctionJSON(stopCount);
+ state.ResumeTiming();
+ optional<SourceFunction<float>> result = conversion::convert<SourceFunction<float>, JSValue>(doc, error);
+ if (!result) {
+ state.SkipWithError(error.message.c_str());
+ }
+ }
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+static void Evaluate_SourceFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+ auto doc = createFunctionJSON(stopCount);
+ conversion::Error error;
+ optional<SourceFunction<float>> function = conversion::convert<SourceFunction<float>, JSValue>(doc, error);
+ if (!function) {
+ state.SkipWithError(error.message.c_str());
+ }
+
+ while(state.KeepRunning()) {
+ function->evaluate(StubGeometryTileFeature(PropertyMap { { "x", static_cast<int64_t>(rand() % 100) } }), -1.0f);
+ }
+
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+BENCHMARK(Parse_SourceFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+BENCHMARK(Evaluate_SourceFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+
diff --git a/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp b/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
new file mode 100644
index 0000000000..e27aeeb48b
--- /dev/null
+++ b/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/util/feature.hpp>
+
+using namespace mbgl;
+
+class StubGeometryTileFeature : public GeometryTileFeature {
+public:
+ StubGeometryTileFeature(PropertyMap properties_)
+ : properties(std::move(properties_)) {
+ }
+
+ StubGeometryTileFeature(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_, PropertyMap properties_)
+ : properties(std::move(properties_)),
+ id(std::move(id_)),
+ type(type_),
+ geometry(std::move(geometry_)) {
+ }
+
+ PropertyMap properties;
+ optional<FeatureIdentifier> id;
+ FeatureType type = FeatureType::Point;
+ GeometryCollection geometry;
+
+ FeatureType getType() const override {
+ return type;
+ }
+
+ optional<FeatureIdentifier> getID() const override {
+ return id;
+ }
+
+ optional<Value> getValue(const std::string& key) const override {
+ return properties.count(key) ? properties.at(key) : optional<Value>();
+ }
+
+ GeometryCollection getGeometries() const override {
+ return geometry;
+ }
+};
diff --git a/circle.yml b/circle.yml
index 207a25fba1..69f5403d9c 100644
--- a/circle.yml
+++ b/circle.yml
@@ -10,9 +10,18 @@ workflows:
ignore: master
- android-debug-arm-v7
- android-release-all
- - node4-clang39-release
- - node6-clang39-release
- - node6-clang39-debug
+ - node4-clang39-release:
+ filters:
+ tags:
+ only: /node-.*/
+ - node6-clang39-release:
+ filters:
+ tags:
+ only: /node-.*/
+ - node6-clang39-debug:
+ filters:
+ tags:
+ only: /node-.*/
- linux-clang39-debug
- linux-clang4-sanitize-address
- linux-clang4-sanitize-undefined
@@ -33,11 +42,11 @@ step-library:
- &restore-cache
restore_cache:
keys:
- - 'v3/{{ .Environment.CIRCLE_JOB }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
- - 'v3/{{ .Environment.CIRCLE_JOB }}/master/{{ checksum ".circle-week" }}'
+ - 'v3/{{ .Environment.CIRCLE_JOB }}/{{ arch }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
+ - 'v3/{{ .Environment.CIRCLE_JOB }}/{{ arch }}/master/{{ checksum ".circle-week" }}'
- &save-cache
save_cache:
- key: 'v3/{{ .Environment.CIRCLE_JOB }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
+ key: 'v3/{{ .Environment.CIRCLE_JOB }}/{{ arch }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
paths: [ "node_modules", "/root/.ccache", "mason_packages/.binaries" ]
@@ -96,6 +105,12 @@ step-library:
command: |
xvfb-run --server-args="-screen 0 1024x768x24" \
logbt -- apitrace trace --api=egl -v make test-node
+ - &run-node-tests-recycle-map
+ run:
+ name: Run node tests (recycling the map object)
+ command: |
+ xvfb-run --server-args="-screen 0 1024x768x24" \
+ logbt -- apitrace trace --api=egl -v make test-node-recycle-map
- &run-unit-tests
run:
name: Run tests
@@ -115,6 +130,10 @@ step-library:
store_artifacts:
path: mapbox-gl-js/test/integration/render-tests/index.html
destination: render-tests
+ - &upload-render-tests-recycle-map
+ store_artifacts:
+ path: mapbox-gl-js/test/integration/render-tests/index-recycle-map.html
+ destination: render-tests
jobs:
# ------------------------------------------------------------------------------
@@ -147,6 +166,7 @@ jobs:
android-debug-arm-v7:
docker:
- image: mbgl/ci@sha256:c34e221294d81da80918d3e9a9df5de795b067e88f86d7c9a5e262763332536e
+ resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -204,7 +224,7 @@ jobs:
(gcloud firebase test android run --type instrumentation \
--app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk \
--test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \
- --device-ids shamu --os-version-ids 22 --locales en --orientations portrait --timeout 15m \
+ --device-ids shamu --os-version-ids 22 --locales en --orientations portrait --timeout 20m \
--test-targets "package com.mapbox.mapboxsdk.testapp.style" 2>&1 | tee firebase.log) || EXIT_CODE=$?
FIREBASE_TEST_BUCKET=$(sed -n 's|^.*\[https://console.developers.google.com/storage/browser/\([^]]*\).*|gs://\1|p' firebase.log)
@@ -236,6 +256,7 @@ jobs:
android-release-all:
docker:
- image: mbgl/ci@sha256:c34e221294d81da80918d3e9a9df5de795b067e88f86d7c9a5e262763332536e
+ resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -286,8 +307,7 @@ jobs:
JOBS: 4
BUILDTYPE: RelWithDebInfo
WITH_EGL: 1
- PACKAGE_JSON_VERSION: $(node -e "console.log(require('./package.json').version)")
- PUBLISH: $([[ "${CIRCLE_BRANCH}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
+ WITH_CXX11ABI: 0
steps:
- checkout
- *generate-cache-key
@@ -310,8 +330,7 @@ jobs:
JOBS: 4
BUILDTYPE: RelWithDebInfo
WITH_EGL: 1
- PACKAGE_JSON_VERSION: $(node -e "console.log(require('./package.json').version)")
- PUBLISH: $([[ "${CIRCLE_BRANCH}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
+ WITH_CXX11ABI: 0
steps:
- checkout
- *generate-cache-key
@@ -334,8 +353,7 @@ jobs:
JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
- PACKAGE_JSON_VERSION: $(node -e "console.log(require('./package.json').version)")
- PUBLISH: $([[ "${CIRCLE_BRANCH}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
+ WITH_CXX11ABI: 0
steps:
- checkout
- *generate-cache-key
@@ -344,9 +362,9 @@ jobs:
- *build-node
- *show-ccache-stats
- *save-cache
- - *run-node-tests
+ - *run-node-tests-recycle-map
- *publish-node-package
- - *upload-render-tests
+ - *upload-render-tests-recycle-map
# ------------------------------------------------------------------------------
linux-clang39-debug:
@@ -464,6 +482,7 @@ jobs:
linux-gcc4.9-debug:
docker:
- image: mbgl/ci:r4-linux-gcc-4.9
+ resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -488,6 +507,7 @@ jobs:
linux-gcc5-debug-coverage:
docker:
- image: mbgl/ci:r4-linux-gcc-5
+ resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -515,6 +535,7 @@ jobs:
linux-gcc5-release-qt4:
docker:
- image: mbgl/ci:r4-linux-gcc-5-qt-4
+ resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
@@ -542,6 +563,7 @@ jobs:
linux-gcc5-release-qt5:
docker:
- image: mbgl/ci:r4-linux-gcc-5-qt-5
+ resource_class: large
working_directory: /src
environment:
LIBSYSCONFCPUS: 4
diff --git a/cloudformation/travis.template b/cloudformation/travis.template
index 29f2402b20..db4e59eb7e 100644
--- a/cloudformation/travis.template
+++ b/cloudformation/travis.template
@@ -130,22 +130,7 @@
}
]
}
- }
- ]
- }
- },
- "BuildUserKey": {
- "Type": "AWS::IAM::AccessKey",
- "Properties": {
- "UserName": {
- "Ref": "BuildUser"
- }
- }
- },
- "BitriseUser": {
- "Type": "AWS::IAM::User",
- "Properties": {
- "Policies": [
+ },
{
"PolicyName": "get-signing-key",
"PolicyDocument": {
@@ -223,11 +208,11 @@
]
}
},
- "BitriseUserKey": {
+ "BuildUserKey": {
"Type": "AWS::IAM::AccessKey",
"Properties": {
"UserName": {
- "Ref": "BitriseUser"
+ "Ref": "BuildUser"
}
}
},
@@ -312,19 +297,6 @@
]
}
},
- "BitriseAccessKeyId": {
- "Value": {
- "Ref": "BitriseUserKey"
- }
- },
- "BitriseSecretAccessKey": {
- "Value": {
- "Fn::GetAtt": [
- "BitriseUserKey",
- "SecretAccessKey"
- ]
- }
- },
"AndroidInstanceProfile": {
"Value": {
"Fn::GetAtt": [
diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake
index f17d95941d..9161209128 100644
--- a/cmake/benchmark-files.cmake
+++ b/cmake/benchmark-files.cmake
@@ -5,6 +5,11 @@ set(MBGL_BENCHMARK_FILES
benchmark/api/query.benchmark.cpp
benchmark/api/render.benchmark.cpp
+ # function
+ benchmark/function/camera_function.benchmark.cpp
+ benchmark/function/composite_function.benchmark.cpp
+ benchmark/function/source_function.benchmark.cpp
+
# include/mbgl
benchmark/include/mbgl/benchmark.hpp
@@ -18,6 +23,7 @@ set(MBGL_BENCHMARK_FILES
# src/mbgl/benchmark
benchmark/src/mbgl/benchmark/benchmark.cpp
+ benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
# util
benchmark/util/dtoa.benchmark.cpp
diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake
index 79e87a0b10..87351e97b1 100644
--- a/cmake/benchmark.cmake
+++ b/cmake/benchmark.cmake
@@ -27,6 +27,8 @@ mbgl_platform_benchmark()
create_source_groups(mbgl-benchmark)
+initialize_xcode_cxx_build_settings(mbgl-benchmark)
+
xcode_create_scheme(
TARGET mbgl-benchmark
OPTIONAL_ARGS
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index 1bbe739256..2eadc747d1 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -8,6 +8,7 @@ set(MBGL_CORE_FILES
include/mbgl/actor/message.hpp
include/mbgl/actor/scheduler.hpp
src/mbgl/actor/mailbox.cpp
+ src/mbgl/actor/scheduler.cpp
# algorithm
src/mbgl/algorithm/covered_by_children.hpp
@@ -112,7 +113,6 @@ set(MBGL_CORE_FILES
src/mbgl/map/transform.hpp
src/mbgl/map/transform_state.cpp
src/mbgl/map/transform_state.hpp
- src/mbgl/map/update.hpp
src/mbgl/map/zoom_history.hpp
# math
@@ -182,7 +182,6 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/possibly_evaluated_property_value.hpp
src/mbgl/renderer/property_evaluation_parameters.hpp
src/mbgl/renderer/property_evaluator.hpp
- src/mbgl/renderer/render_item.hpp
src/mbgl/renderer/render_layer.cpp
src/mbgl/renderer/render_layer.hpp
src/mbgl/renderer/render_light.cpp
@@ -193,9 +192,6 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/render_source_observer.hpp
src/mbgl/renderer/render_static_data.cpp
src/mbgl/renderer/render_static_data.hpp
- src/mbgl/renderer/render_style.cpp
- src/mbgl/renderer/render_style.hpp
- src/mbgl/renderer/render_style_observer.hpp
src/mbgl/renderer/render_tile.cpp
src/mbgl/renderer/render_tile.hpp
src/mbgl/renderer/renderer.cpp
@@ -313,8 +309,6 @@ set(MBGL_CORE_FILES
include/mbgl/storage/resource_transform.hpp
include/mbgl/storage/response.hpp
src/mbgl/storage/asset_file_source.hpp
- src/mbgl/storage/file_source_request.cpp
- src/mbgl/storage/file_source_request.hpp
src/mbgl/storage/http_file_source.hpp
src/mbgl/storage/local_file_source.hpp
src/mbgl/storage/network_status.cpp
@@ -494,8 +488,10 @@ set(MBGL_CORE_FILES
src/mbgl/text/shaping.hpp
# tile
+ include/mbgl/tile/tile_id.hpp
src/mbgl/tile/geojson_tile.cpp
src/mbgl/tile/geojson_tile.hpp
+ src/mbgl/tile/geojson_tile_data.hpp
src/mbgl/tile/geometry_tile.cpp
src/mbgl/tile/geometry_tile.hpp
src/mbgl/tile/geometry_tile_data.cpp
@@ -510,7 +506,7 @@ set(MBGL_CORE_FILES
src/mbgl/tile/tile.hpp
src/mbgl/tile/tile_cache.cpp
src/mbgl/tile/tile_cache.hpp
- src/mbgl/tile/tile_id.hpp
+ src/mbgl/tile/tile_id_hash.cpp
src/mbgl/tile/tile_id_io.cpp
src/mbgl/tile/tile_loader.hpp
src/mbgl/tile/tile_loader_impl.hpp
@@ -553,6 +549,7 @@ set(MBGL_CORE_FILES
include/mbgl/util/run_loop.hpp
include/mbgl/util/size.hpp
include/mbgl/util/string.hpp
+ include/mbgl/util/thread.hpp
include/mbgl/util/tileset.hpp
include/mbgl/util/timer.hpp
include/mbgl/util/traits.hpp
@@ -609,7 +606,6 @@ set(MBGL_CORE_FILES
src/mbgl/util/stopwatch.cpp
src/mbgl/util/stopwatch.hpp
src/mbgl/util/string.cpp
- src/mbgl/util/thread.hpp
src/mbgl/util/thread_local.hpp
src/mbgl/util/throttler.cpp
src/mbgl/util/throttler.hpp
diff --git a/cmake/core.cmake b/cmake/core.cmake
index ff2b4ba53c..c4e711f558 100644
--- a/cmake/core.cmake
+++ b/cmake/core.cmake
@@ -34,3 +34,5 @@ mbgl_platform_core()
create_source_groups(mbgl-core)
xcode_create_scheme(TARGET mbgl-core)
+
+initialize_xcode_cxx_build_settings(mbgl-core)
diff --git a/cmake/filesource.cmake b/cmake/filesource.cmake
new file mode 100644
index 0000000000..bb1b4e8c05
--- /dev/null
+++ b/cmake/filesource.cmake
@@ -0,0 +1,57 @@
+add_library(mbgl-filesource STATIC
+ # File source
+ include/mbgl/storage/default_file_source.hpp
+ platform/default/default_file_source.cpp
+ platform/default/mbgl/storage/file_source_request.hpp
+ platform/default/file_source_request.cpp
+ include/mbgl/storage/online_file_source.hpp
+ platform/default/online_file_source.cpp
+ src/mbgl/storage/http_file_source.hpp
+ src/mbgl/storage/asset_file_source.hpp
+ platform/default/asset_file_source.cpp
+ src/mbgl/storage/local_file_source.hpp
+ platform/default/local_file_source.cpp
+
+ # Offline
+ include/mbgl/storage/offline.hpp
+ platform/default/mbgl/storage/offline.cpp
+ platform/default/mbgl/storage/offline_database.hpp
+ platform/default/mbgl/storage/offline_database.cpp
+ platform/default/mbgl/storage/offline_download.hpp
+ platform/default/mbgl/storage/offline_download.cpp
+
+ # Database
+ platform/default/sqlite3.hpp
+)
+
+target_add_mason_package(mbgl-filesource PUBLIC geometry)
+target_add_mason_package(mbgl-filesource PUBLIC variant)
+target_add_mason_package(mbgl-filesource PUBLIC any)
+target_add_mason_package(mbgl-filesource PRIVATE rapidjson)
+target_add_mason_package(mbgl-filesource PRIVATE boost)
+target_add_mason_package(mbgl-filesource PRIVATE geojson)
+
+set_xcode_property(mbgl-filesource GCC_SYMBOLS_PRIVATE_EXTERN YES)
+
+target_compile_options(mbgl-filesource
+ PRIVATE -fPIC
+ PRIVATE -fvisibility-inlines-hidden
+)
+
+target_include_directories(mbgl-filesource
+ PRIVATE include
+ PRIVATE src
+ PRIVATE platform/default
+)
+
+target_link_libraries(mbgl-filesource
+ PUBLIC mbgl-core
+)
+
+mbgl_filesource()
+
+create_source_groups(mbgl-filesource)
+
+xcode_create_scheme(TARGET mbgl-filesource)
+
+initialize_xcode_cxx_build_settings(mbgl-filesource)
diff --git a/cmake/glfw.cmake b/cmake/glfw.cmake
index dd73b54eef..29d8d2ba94 100644
--- a/cmake/glfw.cmake
+++ b/cmake/glfw.cmake
@@ -34,6 +34,8 @@ mbgl_platform_glfw()
create_source_groups(mbgl-glfw)
+initialize_xcode_cxx_build_settings(mbgl-glfw)
+
xcode_create_scheme(
TARGET mbgl-glfw
OPTIONAL_ARGS
diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake
index 9ef2ddd306..d853950c0d 100644
--- a/cmake/mbgl.cmake
+++ b/cmake/mbgl.cmake
@@ -95,6 +95,16 @@ function(_get_xcconfig_property target var)
get_property(result TARGET ${target} PROPERTY INTERFACE_${var} SET)
if (result)
get_property(result TARGET ${target} PROPERTY INTERFACE_${var})
+ if (var STREQUAL "LINK_LIBRARIES")
+ # Remove target names from the list of linker flags, since Xcode can't deal with them.
+ set(link_flags)
+ foreach(item IN LISTS result)
+ if (NOT TARGET ${item})
+ list(APPEND link_flags ${item})
+ endif()
+ endforeach()
+ set(result "${link_flags}")
+ endif()
string(REPLACE ";-framework " ";-framework;" result "${result}")
string(REPLACE ";" "\" \"" result "${result}")
string(REPLACE "-" "_" target "${target}")
@@ -114,6 +124,32 @@ function(write_xcconfig_target_properties)
)
endfunction()
+# Set Xcode project build settings to be consistent with the CXX flags we're
+# using. (Otherwise, Xcode's defaults may override some of these.)
+macro(initialize_xcode_cxx_build_settings target)
+ # -Wall
+ set_xcode_property(${target} GCC_WARN_SIGN_COMPARE YES)
+ set_xcode_property(${target} GCC_WARN_UNINITIALIZED_AUTOS YES)
+ set_xcode_property(${target} GCC_WARN_UNKNOWN_PRAGMAS YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_FUNCTION YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_LABEL YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_PARAMETER YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_VARIABLE YES)
+
+ # -Wextra
+ set_xcode_property(${target} CLANG_WARN_EMPTY_BODY YES)
+ set_xcode_property(${target} GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS YES)
+
+ # -Wshadow
+ set_xcode_property(${target} GCC_WARN_SHADOW YES)
+
+ # -Wno-unknown-pragmas
+ set_xcode_property(${target} GCC_WARN_UNKNOWN_PRAGMAS YES)
+
+ # -Wnon-virtual-dtor
+ set_xcode_property(${target} GCC_WARN_NON_VIRTUAL_DESTRUCTOR YES)
+endmacro(initialize_xcode_cxx_build_settings)
+
# CMake 3.1 does not have this yet.
set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14")
set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14")
diff --git a/cmake/node.cmake b/cmake/node.cmake
index 502edd8293..c256b13f93 100644
--- a/cmake/node.cmake
+++ b/cmake/node.cmake
@@ -54,6 +54,8 @@ mbgl_platform_node()
create_source_groups(mbgl-node)
+initialize_xcode_cxx_build_settings(mbgl-node)
+
xcode_create_scheme(
TARGET mbgl-node
)
@@ -63,7 +65,7 @@ xcode_create_scheme(
TYPE node
NAME "node tests"
ARGS
- "`npm bin tape`/tape platform/node/test/js/**/*.test.js"
+ "node_modules/.bin/tape platform/node/test/js/**/*.test.js"
)
xcode_create_scheme(
diff --git a/cmake/render.cmake b/cmake/render.cmake
index 407e1ca8ef..f69aed16c0 100644
--- a/cmake/render.cmake
+++ b/cmake/render.cmake
@@ -21,6 +21,8 @@ mbgl_platform_render()
create_source_groups(mbgl-render)
+initialize_xcode_cxx_build_settings(mbgl-render)
+
xcode_create_scheme(
TARGET mbgl-render
OPTIONAL_ARGS
diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake
index 19412e6133..027c34f31e 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -17,6 +17,8 @@ set(MBGL_TEST_FILES
test/api/api_misuse.test.cpp
test/api/custom_layer.test.cpp
test/api/query.test.cpp
+ test/api/recycle_map.cpp
+ test/api/zoom_history.cpp
# gl
test/gl/bucket.test.cpp
diff --git a/cmake/test.cmake b/cmake/test.cmake
index 5e404577ed..c821d53316 100644
--- a/cmake/test.cmake
+++ b/cmake/test.cmake
@@ -41,6 +41,8 @@ mbgl_platform_test()
create_source_groups(mbgl-test)
+initialize_xcode_cxx_build_settings(mbgl-test)
+
xcode_create_scheme(
TARGET mbgl-test
OPTIONAL_ARGS
diff --git a/include/mbgl/actor/actor.hpp b/include/mbgl/actor/actor.hpp
index 74d5a66285..a0df19208e 100644
--- a/include/mbgl/actor/actor.hpp
+++ b/include/mbgl/actor/actor.hpp
@@ -50,19 +50,16 @@ class Actor : public util::noncopyable {
public:
// Enabled for Objects with a constructor taking ActorRef<Object> as the first parameter
- template <typename U = Object, class... Args,
- typename std::enable_if<std::is_constructible<U, ActorRef<Object>, Args...>::value>::type...>
+ template <typename U = Object, class... Args, typename std::enable_if<std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
Actor(Scheduler& scheduler, Args&&... args_)
: mailbox(std::make_shared<Mailbox>(scheduler)),
object(self(), std::forward<Args>(args_)...) {
}
// Enabled for plain Objects
- template <typename U = Object, class... Args,
- typename std::enable_if<!std::is_constructible<U, ActorRef<Object>, Args...>::value>::type...>
- Actor(Scheduler& scheduler, Args&&... args_)
- : mailbox(std::make_shared<Mailbox>(scheduler)),
- object(std::forward<Args>(args_)...) {
+ template<typename U = Object, class... Args, typename std::enable_if<!std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
+ Actor(Scheduler& scheduler, Args&& ... args_)
+ : mailbox(std::make_shared<Mailbox>(scheduler)), object(std::forward<Args>(args_)...) {
}
~Actor() {
diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp
index 406de425d4..0a20993352 100644
--- a/include/mbgl/actor/message.hpp
+++ b/include/mbgl/actor/message.hpp
@@ -64,6 +64,32 @@ public:
std::promise<ResultType> promise;
};
+template <class Object, class MemberFn, class ArgsTuple>
+class AskMessageImpl<void, Object, MemberFn, ArgsTuple> : public Message {
+public:
+ AskMessageImpl(std::promise<void> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
+ : object(object_),
+ memberFn(memberFn_),
+ argsTuple(std::move(argsTuple_)),
+ promise(std::move(promise_)) {
+ }
+
+ void operator()() override {
+ ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>());
+ promise.set_value();
+ }
+
+ template <std::size_t... I>
+ void ask(std::index_sequence<I...>) {
+ (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
+ }
+
+ Object& object;
+ MemberFn memberFn;
+ ArgsTuple argsTuple;
+ std::promise<void> promise;
+};
+
namespace actor {
template <class Object, class MemberFn, class... Args>
diff --git a/include/mbgl/actor/scheduler.hpp b/include/mbgl/actor/scheduler.hpp
index 83689c3348..d8a26ebeab 100644
--- a/include/mbgl/actor/scheduler.hpp
+++ b/include/mbgl/actor/scheduler.hpp
@@ -21,18 +21,21 @@ class Mailbox;
Subject to these constraints, processing can happen on whatever thread in the
pool is available.
- * `RunLoop` is a `Scheduler` that is typically used to create a mailbox and
- `ActorRef` for an object that lives on the main thread and is not itself wrapped
- as an `Actor`:
-
- auto mailbox = std::make_shared<Mailbox>(*util::RunLoop::Get());
+ * `Scheduler::GetCurrent()` is typically used to create a mailbox and `ActorRef`
+ for an object that lives on the main thread and is not itself wrapped an
+ `Actor`. The underlying implementation of this Scheduler should usually be
+ a `RunLoop`
+ auto mailbox = std::make_shared<Mailbox>(*Scheduler::Get());
Actor<Worker> worker(threadPool, ActorRef<Foo>(*this, mailbox));
*/
-
class Scheduler {
public:
virtual ~Scheduler() = default;
virtual void schedule(std::weak_ptr<Mailbox>) = 0;
+
+ // Set/Get the current Scheduler for this thread
+ static Scheduler* GetCurrent();
+ static void SetCurrent(Scheduler*);
};
} // namespace mbgl
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 7d6678dc93..c5f90d99e1 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -8,6 +8,7 @@
#include <mbgl/util/size.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/map/camera.hpp>
+#include <mbgl/util/geometry.hpp>
#include <cstdint>
#include <string>
@@ -42,7 +43,8 @@ public:
// Register a callback that will get called (on the render thread) when all resources have
// been loaded and a complete render occurs.
using StillImageCallback = std::function<void (std::exception_ptr)>;
- void renderStill(StillImageCallback callback);
+ void renderStill(StillImageCallback);
+ void renderStill(const CameraOptions&, MapDebugOptions, StillImageCallback);
// Triggers a repaint.
void triggerRepaint();
@@ -65,6 +67,10 @@ public:
void jumpTo(const CameraOptions&);
void easeTo(const CameraOptions&, const AnimationOptions&);
void flyTo(const CameraOptions&, const AnimationOptions&);
+ CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&, optional<double> bearing = {}) const;
+ CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&, optional<double> bearing = {}) const;
+ CameraOptions cameraForGeometry(const Geometry<double>&, const EdgeInsets&, optional<double> bearing = {}) const;
+ LatLngBounds latLngBoundsForCamera(const CameraOptions&) const;
// Position
void moveBy(const ScreenCoordinate&, const AnimationOptions& = {});
@@ -81,9 +87,6 @@ public:
double getZoom() const;
void setLatLngZoom(const LatLng&, double zoom, const AnimationOptions& = {});
void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const AnimationOptions& = {});
- CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&) const;
- CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&) const;
- LatLngBounds latLngBoundsForCamera(const CameraOptions&) const;
void resetZoom();
// Bounds
diff --git a/include/mbgl/math/log2.hpp b/include/mbgl/math/log2.hpp
index 8a3bc7f1c0..53d5e45545 100644
--- a/include/mbgl/math/log2.hpp
+++ b/include/mbgl/math/log2.hpp
@@ -17,9 +17,9 @@ typename std::enable_if_t<std::is_floating_point<T>::value, T> log2(T x)
// log2() is producing wrong results on ARMv5 binaries
// running on ARMv7+ CPUs.
#if defined(__ANDROID__)
- return std::log(x) / M_LN2;
+ return ::log(x) / M_LN2;
#else
- return std::log2(x);
+ return ::log2(x);
#endif
}
diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp
index 95828a1b79..be8abb2c29 100644
--- a/include/mbgl/renderer/renderer.hpp
+++ b/include/mbgl/renderer/renderer.hpp
@@ -28,6 +28,8 @@ public:
const optional<std::string> programCacheDir = {});
~Renderer();
+ void markContextLost();
+
void setObserver(RendererObserver*);
void render(const UpdateParameters&);
diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp
index f7d19a1791..295838c71b 100644
--- a/include/mbgl/renderer/renderer_backend.hpp
+++ b/include/mbgl/renderer/renderer_backend.hpp
@@ -35,6 +35,8 @@ public:
// set to the current state.
virtual void bind() = 0;
+ virtual Size getFramebufferSize() const = 0;
+
protected:
// Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations
// must call the API-specific version that obtains the function pointer for this function,
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index 9911e0ce67..91e442cf85 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -140,6 +140,7 @@ public:
void resume();
// For testing only.
+ void setOnlineStatus(bool);
void put(const Resource&, const Response&);
class Impl;
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index 818cfe2ba5..117dd0591b 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -31,12 +31,14 @@ public:
/* Private */
std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
-
+ uint64_t tileCount(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
const std::string styleURL;
const LatLngBounds bounds;
const double minZoom;
const double maxZoom;
const float pixelRatio;
+private:
+ Range<uint8_t> coveringZoomRange(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
};
/*
diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp
index ffd75662e6..28d70ce544 100644
--- a/include/mbgl/storage/online_file_source.hpp
+++ b/include/mbgl/storage/online_file_source.hpp
@@ -24,6 +24,9 @@ public:
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
+ // For testing only.
+ void setOnlineStatus(bool);
+
private:
friend class OnlineFileRequest;
diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp
index 7e9ced8049..5d44f4869f 100644
--- a/include/mbgl/storage/resource.hpp
+++ b/include/mbgl/storage/resource.hpp
@@ -68,6 +68,7 @@ public:
optional<Timestamp> priorModified = {};
optional<Timestamp> priorExpires = {};
optional<std::string> priorEtag = {};
+ std::shared_ptr<const std::string> priorData;
};
} // namespace mbgl
diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp
index 32fe4e0c8a..508400141b 100644
--- a/include/mbgl/storage/response.hpp
+++ b/include/mbgl/storage/response.hpp
@@ -2,7 +2,6 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/variant.hpp>
#include <string>
#include <memory>
@@ -27,6 +26,10 @@ public:
// This is set to true for 304 Not Modified responses.
bool notModified = false;
+ // This is set to true when the server requested that no expired resources be used by
+ // specifying "Cache-Control: must-revalidate".
+ bool mustRevalidate = false;
+
// The actual data of the response. Present only for non-error, non-notModified responses.
std::shared_ptr<const std::string> data;
@@ -37,6 +40,12 @@ public:
bool isFresh() const {
return expires ? *expires > util::now() : !error;
}
+
+ // Indicates whether we are allowed to use this response according to HTTP caching rules.
+ // It may or may not be stale.
+ bool isUsable() const {
+ return !mustRevalidate || (expires && *expires > util::now());
+ }
};
class Response::Error {
diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp
index 19383d90ce..1c9c18250c 100644
--- a/include/mbgl/style/conversion/geojson_options.hpp
+++ b/include/mbgl/style/conversion/geojson_options.hpp
@@ -14,6 +14,16 @@ struct Converter<GeoJSONOptions> {
optional<GeoJSONOptions> operator()(const V& value, Error& error) const {
GeoJSONOptions options;
+ const auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ if (toNumber(*minzoomValue)) {
+ options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
+ } else {
+ error = { "GeoJSON source minzoom value must be a number" };
+ return {};
+ }
+ }
+
const auto maxzoomValue = objectMember(value, "maxzoom");
if (maxzoomValue) {
if (toNumber(*maxzoomValue)) {
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp
index 9252297d75..59b0e7be32 100644
--- a/include/mbgl/style/conversion/make_property_setters.hpp
+++ b/include/mbgl/style/conversion/make_property_setters.hpp
@@ -45,17 +45,18 @@ auto makeLayoutPropertySetters() {
result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
+ result["icon-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
- result["text-max-width"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
+ result["text-max-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
- result["text-letter-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
+ result["text-letter-spacing"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
result["text-justify"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
- result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextAnchorType>, &SymbolLayer::setTextAnchor>;
+ result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp
index 79a353b047..bf3387f95b 100644
--- a/include/mbgl/style/layers/custom_layer.hpp
+++ b/include/mbgl/style/layers/custom_layer.hpp
@@ -39,6 +39,14 @@ struct CustomLayerRenderParameters {
using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&);
/**
+ * Called when the system has destroyed the underlying GL context. The
+ * `CustomLayerDeinitializeFunction` will not be called in this case, however
+ * `CustomLayerInitializeFunction` will be called instead to prepare for a new render.
+ *
+ */
+using CustomLayerContextLostFunction = void (*)(void* context);
+
+/**
* Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This
* method is called once, from the main thread, at a point when the GL context is active.
*
@@ -51,8 +59,16 @@ public:
CustomLayer(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
+ CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);
+
+ CustomLayer(const std::string& id,
+ CustomLayerInitializeFunction,
+ CustomLayerRenderFunction,
+ CustomLayerDeinitializeFunction,
+ void* context);
+
~CustomLayer() final;
// Visibility
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index 6e355c0057..a72baa0b4e 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -98,6 +98,10 @@ public:
DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const;
void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>);
+ static DataDrivenPropertyValue<SymbolAnchorType> getDefaultIconAnchor();
+ DataDrivenPropertyValue<SymbolAnchorType> getIconAnchor() const;
+ void setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType>);
+
static PropertyValue<AlignmentType> getDefaultIconPitchAlignment();
PropertyValue<AlignmentType> getIconPitchAlignment() const;
void setIconPitchAlignment(PropertyValue<AlignmentType>);
@@ -122,25 +126,25 @@ public:
DataDrivenPropertyValue<float> getTextSize() const;
void setTextSize(DataDrivenPropertyValue<float>);
- static PropertyValue<float> getDefaultTextMaxWidth();
- PropertyValue<float> getTextMaxWidth() const;
- void setTextMaxWidth(PropertyValue<float>);
+ static DataDrivenPropertyValue<float> getDefaultTextMaxWidth();
+ DataDrivenPropertyValue<float> getTextMaxWidth() const;
+ void setTextMaxWidth(DataDrivenPropertyValue<float>);
static PropertyValue<float> getDefaultTextLineHeight();
PropertyValue<float> getTextLineHeight() const;
void setTextLineHeight(PropertyValue<float>);
- static PropertyValue<float> getDefaultTextLetterSpacing();
- PropertyValue<float> getTextLetterSpacing() const;
- void setTextLetterSpacing(PropertyValue<float>);
+ static DataDrivenPropertyValue<float> getDefaultTextLetterSpacing();
+ DataDrivenPropertyValue<float> getTextLetterSpacing() const;
+ void setTextLetterSpacing(DataDrivenPropertyValue<float>);
static DataDrivenPropertyValue<TextJustifyType> getDefaultTextJustify();
DataDrivenPropertyValue<TextJustifyType> getTextJustify() const;
void setTextJustify(DataDrivenPropertyValue<TextJustifyType>);
- static DataDrivenPropertyValue<TextAnchorType> getDefaultTextAnchor();
- DataDrivenPropertyValue<TextAnchorType> getTextAnchor() const;
- void setTextAnchor(DataDrivenPropertyValue<TextAnchorType>);
+ static DataDrivenPropertyValue<SymbolAnchorType> getDefaultTextAnchor();
+ DataDrivenPropertyValue<SymbolAnchorType> getTextAnchor() const;
+ void setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType>);
static PropertyValue<float> getDefaultTextMaxAngle();
PropertyValue<float> getTextMaxAngle() const;
diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp
index 2dcfec51aa..5bdf1ef957 100644
--- a/include/mbgl/style/sources/geojson_source.hpp
+++ b/include/mbgl/style/sources/geojson_source.hpp
@@ -12,6 +12,7 @@ namespace style {
struct GeoJSONOptions {
// GeoJSON-VT options
+ uint8_t minzoom = 0;
uint8_t maxzoom = 18;
uint16_t buffer = 128;
double tolerance = 0.375;
diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp
index cb84922b4d..d6fdbd8f2c 100644
--- a/include/mbgl/style/style.hpp
+++ b/include/mbgl/style/style.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/style/transition_options.hpp>
+#include <mbgl/map/camera.hpp>
#include <mbgl/util/geo.hpp>
#include <string>
@@ -32,10 +33,7 @@ public:
// Defaults
std::string getName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
+ CameraOptions getDefaultCamera() const;
// TransitionOptions
TransitionOptions getTransitionOptions() const;
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index 44b16f16e7..ec7358de8c 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -68,7 +68,7 @@ enum class TextJustifyType : uint8_t {
Right
};
-enum class TextAnchorType : uint8_t {
+enum class SymbolAnchorType : uint8_t {
Center,
Left,
Right,
diff --git a/src/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp
index 811158e9b9..0457dd3a07 100644
--- a/src/mbgl/tile/tile_id.hpp
+++ b/include/mbgl/tile/tile_id.hpp
@@ -4,11 +4,11 @@
#include <cstdint>
#include <array>
+#include <tuple>
#include <forward_list>
#include <algorithm>
#include <iosfwd>
#include <cassert>
-#include <boost/functional/hash.hpp>
namespace mbgl {
@@ -244,32 +244,19 @@ inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const fl
namespace std {
-template <> struct hash<mbgl::CanonicalTileID> {
- size_t operator()(const mbgl::CanonicalTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, id.x);
- boost::hash_combine(seed, id.y);
- boost::hash_combine(seed, id.z);
- return seed;
- }
+template <>
+struct hash<mbgl::CanonicalTileID> {
+ size_t operator()(const mbgl::CanonicalTileID& id) const;
};
-template <> struct hash<mbgl::UnwrappedTileID> {
- size_t operator()(const mbgl::UnwrappedTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
- boost::hash_combine(seed, id.wrap);
- return seed;
- }
+template <>
+struct hash<mbgl::UnwrappedTileID> {
+ size_t operator()(const mbgl::UnwrappedTileID& id) const;
};
-template <> struct hash<mbgl::OverscaledTileID> {
- size_t operator()(const mbgl::OverscaledTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
- boost::hash_combine(seed, id.overscaledZ);
- return seed;
- }
+template <>
+struct hash<mbgl::OverscaledTileID> {
+ size_t operator()(const mbgl::OverscaledTileID& id) const;
};
} // namespace std
diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp
index c2250c6f2e..d5e55065c4 100644
--- a/include/mbgl/util/constants.hpp
+++ b/include/mbgl/util/constants.hpp
@@ -19,7 +19,8 @@ constexpr float tileSize = 512;
*
* Positions are stored as signed 16bit integers.
* One bit is lost for signedness to support features extending past the left edge of the tile.
- * One bit is lost because the line vertex buffer packs 1 bit of other data into the int.
+ * One bit is lost because the line vertex buffer used to pack 1 bit of other data into the int.
+ * This is no longer the case but we're reserving this bit anyway.
* One bit is lost to support features extending past the extent on the right edge of the tile.
* This leaves us with 2^13 = 8192
*/
@@ -37,6 +38,7 @@ constexpr double MIN_ZOOM = 0.0;
constexpr double MAX_ZOOM = 25.5;
constexpr float MIN_ZOOM_F = MIN_ZOOM;
constexpr float MAX_ZOOM_F = MAX_ZOOM;
+constexpr uint8_t DEFAULT_MAX_ZOOM = 22;
constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4;
@@ -59,7 +61,6 @@ extern const bool tileParseWarnings;
extern const bool styleParseWarnings;
extern const bool spriteWarnings;
extern const bool renderWarnings;
-extern const bool renderTree;
extern const bool labelTextMissingWarning;
extern const bool missingFontStackWarning;
extern const bool missingFontFaceWarning;
diff --git a/include/mbgl/util/geometry.hpp b/include/mbgl/util/geometry.hpp
index 6dc16bc514..a28c59a47d 100644
--- a/include/mbgl/util/geometry.hpp
+++ b/include/mbgl/util/geometry.hpp
@@ -2,6 +2,7 @@
#include <mapbox/geometry/geometry.hpp>
#include <mapbox/geometry/point_arithmetic.hpp>
+#include <mapbox/geometry/for_each_point.hpp>
namespace mbgl {
@@ -58,4 +59,9 @@ struct ToFeatureType {
FeatureType operator()(const mapbox::geometry::geometry_collection<T> &) const { return FeatureType::Unknown; }
};
+template <class T, typename F>
+auto forEachPoint(const Geometry<T>& g, F f) {
+ mapbox::geometry::for_each_point(g, f);
+}
+
} // namespace mbgl
diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp
index cb28f3da8d..4887058f79 100644
--- a/include/mbgl/util/image.hpp
+++ b/include/mbgl/util/image.hpp
@@ -5,6 +5,7 @@
#include <mbgl/util/size.hpp>
#include <string>
+#include <cstring>
#include <memory>
#include <algorithm>
@@ -91,6 +92,31 @@ public:
operator=(std::move(newImage));
}
+ // Clears the rect area specified by `pt` and `size` from `dstImage`.
+ static void clear(Image& dstImg, const Point<uint32_t>& pt, const Size& size) {
+ if (size.isEmpty()) {
+ return;
+ }
+
+ if (!dstImg.valid()) {
+ throw std::invalid_argument("invalid destination for image clear");
+ }
+
+ if (size.width > dstImg.size.width ||
+ size.height > dstImg.size.height ||
+ pt.x > dstImg.size.width - size.width ||
+ pt.y > dstImg.size.height - size.height) {
+ throw std::out_of_range("out of range destination coordinates for image clear");
+ }
+
+ uint8_t* dstData = dstImg.data.get();
+
+ for (uint32_t y = 0; y < size.height; y++) {
+ const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels;
+ std::memset(dstData + dstOffset, 0, size.width * channels);
+ }
+ }
+
// Copy image data within `rect` from `src` to the rectangle of the same size at `pt`
// in `dst`. If the specified bounds exceed the bounds of the source or destination,
// throw `std::out_of_range`. Must not be used to move data within a single Image.
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index 3cc1146513..f64502c5bc 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -75,10 +75,7 @@ public:
}
static Point<double> project(const LatLng& latLng, double scale) {
- return Point<double> {
- util::LONGITUDE_MAX + latLng.longitude(),
- util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX))
- } * worldSize(scale) / util::DEGREES_MAX;
+ return project_(latLng, worldSize(scale));
}
static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) {
@@ -89,6 +86,23 @@ public:
wrapMode
};
}
+
+ // Project lat, lon to point in a zoom-dependent world size
+ static Point<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) {
+ const double t2z = tileSize * std::pow(2, zoom);
+ Point<double> pt = project_(point, t2z);
+ // Flip y coordinate
+ auto x = std::round(std::min(pt.x, t2z));
+ auto y = std::round(std::min(t2z - pt.y, t2z));
+ return { x, y };
+ }
+private:
+ static Point<double> project_(const LatLng& latLng, double worldSize) {
+ return Point<double> {
+ util::LONGITUDE_MAX + latLng.longitude(),
+ util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX))
+ } * worldSize / util::DEGREES_MAX;
+ }
};
} // namespace mbgl
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp
index 14352ca823..acbea80273 100644
--- a/include/mbgl/util/run_loop.hpp
+++ b/include/mbgl/util/run_loop.hpp
@@ -62,6 +62,12 @@ public:
push(task);
return std::make_unique<WorkRequest>(task);
}
+
+ void schedule(std::weak_ptr<Mailbox> mailbox) override {
+ invoke([mailbox] () {
+ Mailbox::maybeReceive(mailbox);
+ });
+ }
class Impl;
@@ -72,12 +78,6 @@ private:
void push(std::shared_ptr<WorkTask>);
- void schedule(std::weak_ptr<Mailbox> mailbox) override {
- invoke([mailbox] () {
- Mailbox::maybeReceive(mailbox);
- });
- }
-
void withMutex(std::function<void()>&& fn) {
std::lock_guard<std::mutex> lock(mutex);
fn();
diff --git a/src/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp
index 572f46080e..672eebf6db 100644
--- a/src/mbgl/util/thread.hpp
+++ b/include/mbgl/util/thread.hpp
@@ -61,8 +61,6 @@ public:
}
~Thread() override {
- MBGL_VERIFY_THREAD(tid);
-
if (paused) {
resume();
}
diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp
index 2fa19d3f53..61aa47d4ea 100644
--- a/include/mbgl/util/tileset.hpp
+++ b/include/mbgl/util/tileset.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/util/range.hpp>
+#include <mbgl/util/constants.hpp>
#include <vector>
#include <string>
@@ -18,7 +19,7 @@ public:
Scheme scheme;
Tileset(std::vector<std::string> tiles_ = std::vector<std::string>(),
- Range<uint8_t> zoomRange_ = { 0, 22 },
+ Range<uint8_t> zoomRange_ = { 0, util::DEFAULT_MAX_ZOOM },
std::string attribution_ = {},
Scheme scheme_ = Scheme::XYZ)
: tiles(std::move(tiles_)),
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject 9d72cc2c461673525f1a6321314d2460c0f3324
+Subproject 3634d945477cb7dbbf1e0bebf4cf51542821895
diff --git a/package.json b/package.json
index 0cd6b450c7..e398121d18 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@mapbox/mapbox-gl-native",
- "version": "3.5.5",
+ "version": "3.5.7",
"description": "Renders map tiles with Mapbox GL",
"keywords": [
"mapbox",
@@ -13,15 +13,18 @@
},
"license": "BSD-2-Clause",
"dependencies": {
- "node-pre-gyp": "^0.6.28",
- "nan": "^2.4.0"
+ "nan": "^2.4.0",
+ "node-pre-gyp": "^0.6.36",
+ "npm-run-all": "^4.0.2"
},
"devDependencies": {
"aws-sdk": "^2.3.5",
"csscolorparser": "^1.0.2",
"ejs": "^2.4.1",
"express": "^4.11.1",
+ "flow-remove-types": "^1.2.1",
"lodash": "^4.16.4",
+ "mapbox-gl-styles": "2.0.2",
"pixelmatch": "^4.0.2",
"pngjs": "^3.0.0",
"request": "^2.72.0",
@@ -35,7 +38,9 @@
"install": "node-pre-gyp install --fallback-to-build=false || make node",
"test": "tape platform/node/test/js/**/*.test.js",
"test-memory": "node --expose-gc platform/node/test/memory.test.js",
- "test-suite": "node platform/node/test/render.test.js && node platform/node/test/query.test.js"
+ "test-suite": "run-s test-render test-query",
+ "test-render": "node platform/node/test/render.test.js",
+ "test-query": "node platform/node/test/query.test.js"
},
"gypfile": true,
"binary": {
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index fa54b1e3ed..ff03df2236 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -5,6 +5,68 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
## 5.2.0 - TBA
* Add support for ImageSource [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+* Added `MapboxMap.getCameraForGeometry()` to get a camera with zoom level and center coordinate computed to fit a shape. ([#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107))
+
+### 5.1.4 - September 25, 2017
+
+* Update translations [#10033](https://github.com/mapbox/mapbox-gl-native/pull/10033) & [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
+* Continue rendering tiles despite erros [#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012)
+* Fix layer z-fighting [#9942](https://github.com/mapbox/mapbox-gl-native/pull/9942)
+* Align line vertex to 4-byte boundary [#9943](https://github.com/mapbox/mapbox-gl-native/pull/9943)
+* Bump proguard config for OkHttp [#9970](https://github.com/mapbox/mapbox-gl-native/pull/9970)
+* Remove database on schema downgrade [#9837](https://github.com/mapbox/mapbox-gl-native/pull/9837)
+* Disable rotation gesture when pinch zooming [#10026](https://github.com/mapbox/mapbox-gl-native/pull/10026)
+* Do not check if connection is local request [#9968](https://github.com/mapbox/mapbox-gl-native/pull/9968)
+* Harden offline region deletion [#9967](https://github.com/mapbox/mapbox-gl-native/pull/9967)
+* Clear out mapCallback's OnMapReadyListeners on destroy [#9957](https://github.com/mapbox/mapbox-gl-native/pull/9957)
+* Avoid adding duplicate points to bounds [#9955](https://github.com/mapbox/mapbox-gl-native/pull/9955)
+* Download is complete fix [#9913](https://github.com/mapbox/mapbox-gl-native/pull/9913)
+* MAS 2.2.3 [#9901](https://github.com/mapbox/mapbox-gl-native/pull/9901)
+* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
+
+## 5.1.3 - August 18, 2017
+
+* Use separate attribute component for line normals [#9753](https://github.com/mapbox/mapbox-gl-native/pull/9753)
+* Track state of initial overlain views margins [#9391](https://github.com/mapbox/mapbox-gl-native/pull/9391)
+* Compability for Samsung devices forcing 3-4 array vector length [#9746](https://github.com/mapbox/mapbox-gl-native/pull/9746)
+* Smallest LatLngBounds when visible region crosses dateline [#9747](https://github.com/mapbox/mapbox-gl-native/pull/9747)
+* Readd ProjectMeters [#9766](https://github.com/mapbox/mapbox-gl-native/pull/9766)
+* Enable location change animation of MyLocationView by default [#9779](https://github.com/mapbox/mapbox-gl-native/pull/9779)
+* Avoid IndexOutOfBoundsException when destroying map object [#9789](https://github.com/mapbox/mapbox-gl-native/pull/9789)
+* MAS 2.2.1 [#9796](https://github.com/mapbox/mapbox-gl-native/pull/9796)
+
+## 5.1.2 - August 2, 2017
+
+* Disable program caching on Ardreno GPU 3xx, 4xx and 5xx [#9625](https://github.com/mapbox/mapbox-gl-native/pull/9625)
+* GeoJSON proguard issue [#9577](https://github.com/mapbox/mapbox-gl-native/pull/9577)
+* Harden click events of shape annotations [#9585](https://github.com/mapbox/mapbox-gl-native/pull/9585)
+* Validate Marker before opening InfoWindow [#9586](https://github.com/mapbox/mapbox-gl-native/pull/9586)
+* Fix added to map check [#9602](https://github.com/mapbox/mapbox-gl-native/pull/9602)
+* Don't query shape annotations if none were added [#9606](https://github.com/mapbox/mapbox-gl-native/pull/9606)
+* Fix compass direction [#9632](https://github.com/mapbox/mapbox-gl-native/pull/9632)
+* Remove preview image integration [#9657](https://github.com/mapbox/mapbox-gl-native/pull/9657)
+* Try/catch initialization of telemetry [#9658](https://github.com/mapbox/mapbox-gl-native/pull/9658)
+* Fix typo OnCameraMoveStartedListener [#9664](https://github.com/mapbox/mapbox-gl-native/pull/9664)
+* Bump MAS dependency to 5.2.0 [#9671](https://github.com/mapbox/mapbox-gl-native/pull/9671)
+
+## 5.1.1 - July 21, 2017
+
+* Rework attribution binding [#9433](https://github.com/mapbox/mapbox-gl-native/pull/9433)
+* BackendScope changes [#9538](https://github.com/mapbox/mapbox-gl-native/pull/9538)
+* Invisible MarkerView performance fix [#9420](https://github.com/mapbox/mapbox-gl-native/pull/9420)
+* Polyline/Polygon click listeners [#9443](https://github.com/mapbox/mapbox-gl-native/pull/9443)
+* Hit test Marker/MarkerViews [#9424](https://github.com/mapbox/mapbox-gl-native/pull/9424)
+* Fine tune fling gesture [#9532](https://github.com/mapbox/mapbox-gl-native/pull/9532)
+* Bump OkHttp 3.8.0, Android 7.x crashes [#9522](https://github.com/mapbox/mapbox-gl-native/pull/9522)
+* MyLocationView tint fix [#9410](https://github.com/mapbox/mapbox-gl-native/pull/9410)
+* VisibleRegion logic [#9428](https://github.com/mapbox/mapbox-gl-native/pull/9428)
+* Validate motion event [#9434](https://github.com/mapbox/mapbox-gl-native/pull/9434)
+* Add French localization [#9545](https://github.com/mapbox/mapbox-gl-native/pull/9545)
+* Fix public.xml [#9525](https://github.com/mapbox/mapbox-gl-native/pull/9525/files)
+* Manage InfoWindow selection in AnnotationManager [#9567](https://github.com/mapbox/mapbox-gl-native/pull/9567)
+* Increase touch target size [#9565](https://github.com/mapbox/mapbox-gl-native/pull/9565)
+* Work around Adreno 2xx GPU bugs [#9573](https://github.com/mapbox/mapbox-gl-native/pull/9573)
## 5.1.0 - June 30, 2017
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index 0cb6e529f1..c96587fce6 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -41,11 +41,14 @@ android {
// to invoke the Java tests. When we explicitly specify an ABI of 'none', no native dependencies are
// added. When another ABI is specified explicitly, we're just going to build that ABI. In all other
// cases, all ABIs are built.
- // When invoking from the command line, set `-Pmapbox.abis=...` to only build the desired architectures.
+ //
+ // When invoking from the command line or to override the device default, set `-Pmapbox.abis=...` to
+ // only build the desired architectures.
+ //
// When building from Android Studio, gradle.properties sets `android.buildOnlyTargetAbi=true` so that
// only the architecture for the device you're running on gets built.
def abi = 'all'
- if (!project.hasProperty('android.injected.invoked.from.ide')) {
+ if (!project.hasProperty('android.injected.invoked.from.ide') || project.hasProperty("mapbox.abis")) {
// Errors when the user invokes Gradle from the command line and didn't set mapbox.abis
abi = project.getProperty("mapbox.abis")
}
@@ -116,9 +119,10 @@ android {
}
lintOptions {
- baseline file("lint-baseline.xml")
+ disable 'MissingTranslation', 'TypographyQuotes'
+ baseline file("lint-baseline-local.xml")
checkAllWarnings true
- warningsAsErrors true
+ warningsAsErrors false
}
testOptions {
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index ff6e2f61df..a3dab0eff6 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -17,4 +17,5 @@ POM_PACKAGING=aar
# Only build native dependencies for the current ABI
# See https://code.google.com/p/android/issues/detail?id=221098#c20
-android.buildOnlyTargetAbi=true \ No newline at end of file
+android.buildOnlyTargetAbi=true
+
diff --git a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml
new file mode 100644
index 0000000000..0a76f53505
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 2.3.1">
+
+ <issue
+ id="MissingTranslation"
+ message="&quot;`mapbox_attributionErrorNoBrowser`&quot; is not translated in &quot;ca&quot; (Catalan), &quot;es&quot; (Spanish), &quot;lt&quot; (Lithuanian), &quot;nl&quot; (Dutch), &quot;sv&quot; (Swedish), &quot;vi&quot; (Vietnamese)"
+ errorLine1=" &lt;string name=&quot;mapbox_attributionErrorNoBrowser&quot;>No web browser installed on device, can\&apos;t open web page.&lt;/string>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/values/strings.xml"
+ line="13"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="MissingTranslation"
+ message="&quot;`mapbox_telemetrySettings`&quot; is not translated in &quot;ca&quot; (Catalan), &quot;es&quot; (Spanish), &quot;lt&quot; (Lithuanian), &quot;nl&quot; (Dutch), &quot;sv&quot; (Swedish), &quot;vi&quot; (Vietnamese)"
+ errorLine1=" &lt;string name=&quot;mapbox_telemetrySettings&quot;>Telemetry Settings&lt;/string>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/values/strings.xml"
+ line="15"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="TypographyQuotes"
+ message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?"
+ errorLine1=" &lt;string name=&quot;mapbox_attributionTelemetryMessage&quot;>Estàs ajudant a millorar els mapes d\&apos;OpenStreetMap i de Mapbox aportant dades d\&apos;ús anònimes.&lt;/string>"
+ errorLine2=" ^">
+ <location
+ file="src/main/res/values-ca/strings.xml"
+ line="9"
+ column="55"/>
+ </issue>
+
+</issues>
diff --git a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml
index 4dc8ecde70..fd65c9f627 100644
--- a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml
+++ b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- REMEMBER! First you run Lint locally you'll need to move the lint-baseline.xml file
+<!-- REMEMBER! First you run Lint locally you'll need to move lint-baseline-local.xml.xml file
generated into the lint folder and called it lint-baseline-local.xml
If you remove any error when running Lint locally, you'll get a warning from the command
line advising you to remove it from the baseline. If you remove it (remember to remove it
@@ -9,94 +9,6 @@
<issues format="4" by="lint 2.3.1">
<issue
- id="MissingPermission"
- message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
- errorLine1=" Location lastKnownLocation = locationEngine.getLastLocation();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java"
- line="783"
- column="38"/>
- </issue>
-
- <issue
- id="MissingPermission"
- message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
- errorLine1=" locationEngine.requestLocationUpdates();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java"
- line="787"
- column="9"/>
- </issue>
-
- <issue
- id="DefaultLocale"
- message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
- errorLine1=" Timber.v(String.format(&quot;[HTTP] Request was successful (code = %d).&quot;, response.code()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="112"
- column="16"/>
- </issue>
-
- <issue
- id="DefaultLocale"
- message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
- errorLine1=" Timber.d(String.format("
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="116"
- column="16"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to read the package metadata: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to read the package metadata: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="75"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to read the storage key: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to read the storage key: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="77"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to obtain the external storage path: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to obtain the external storage path: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="86"
- column="9"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to delete old ambient cache database: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to delete old ambient cache database: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="113"
- column="11"/>
- </issue>
-
- <issue
id="MissingTranslation"
message="&quot;`mapbox_attributionErrorNoBrowser`&quot; is not translated in &quot;ca&quot; (Catalan), &quot;es&quot; (Spanish), &quot;lt&quot; (Lithuanian), &quot;nl&quot; (Dutch), &quot;sv&quot; (Swedish), &quot;vi&quot; (Vietnamese)"
errorLine1=" &lt;string name=&quot;mapbox_attributionErrorNoBrowser&quot;>No web browser installed on device, can\&apos;t open web page.&lt;/string>"
@@ -119,431 +31,6 @@
</issue>
<issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.v(&quot;Connected: &quot; + connected);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java"
- line="85"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Device returned an out of range width size, &quot;"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java"
- line="178"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Device returned an out of range height size, &quot;"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java"
- line="185"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Old ambient cache database deleted to save space: &quot; + path);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="110"
- column="22"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Not setting minZoomPreference, value is in unsupported range: &quot; + minZoom);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/Transform.java"
- line="349"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Not setting maxZoomPreference, value is in unsupported range: &quot; + maxZoom);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/Transform.java"
- line="361"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(&quot;Stops: %s is a different type: %s&quot;, stops.getClass(), exception));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java"
- line="282"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.v(String.format(&quot;[HTTP] Request was successful (code = %d).&quot;, response.code()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="112"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.d(String.format("
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="116"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="163"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="166"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.w(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="170"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(&quot;Layer: %s is a different type: %s&quot;, layerId, exception));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java"
- line="286"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(&quot;Source: %s is a different type: %s&quot;, sourceId, exception));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java"
- line="407"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java"
- line="92"
- column="16"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to read the package metadata: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="75"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to read the storage key: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="77"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to obtain the external storage path: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="86"
- column="9"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to delete old ambient cache database: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="113"
- column="11"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `ConnectivityReceiver` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" private static ConnectivityReceiver INSTANCE;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java"
- line="24"
- column="3"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `IconFactory` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" private static IconFactory instance;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java"
- line="38"
- column="3"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `Mapbox` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" private static Mapbox INSTANCE;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/Mapbox.java"
- line="29"
- column="3"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `OfflineManager` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" // This object is implemented as a singleton"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="42"
- column="3"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.color.mapbox_my_location_ring` appears to be unused"
- errorLine1=" &lt;color name=&quot;mapbox_my_location_ring&quot;>@color/mapbox_blue&lt;/color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/colors.xml"
- line="6"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_infowindow_offset` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_infowindow_offset&quot;>-2dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="5"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_infowindow_line_width` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_infowindow_line_width&quot;>1.5dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="6"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_left_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_left_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="7"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_top_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_top_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="8"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_right_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_right_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="9"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_bottom_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_bottom_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="10"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_two_dp` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_two_dp&quot;>2dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="11"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_ten_dp` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_ten_dp&quot;>10dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="14"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_sixteen_dp` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_sixteen_dp&quot;>16dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="15"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.drawable.mapbox_infowindow_icon_bg` appears to be unused">
- <location
- file="src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.color.mapbox_material_bg_selector` appears to be unused"
- errorLine1="&lt;selector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;>"
- errorLine2="^">
- <location
- file="src/main/res/color/mapbox_material_bg_selector.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.string.mapbox_style_outdoors` appears to be unused"
- errorLine1=" &lt;string name=&quot;mapbox_style_outdoors&quot; translatable=&quot;false&quot;>mapbox://styles/mapbox/outdoors-v10&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/strings.xml"
- line="22"
- column="13"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.string.mapbox_style_traffic_day` appears to be unused"
- errorLine1=" &lt;string name=&quot;mapbox_style_traffic_day&quot; translatable=&quot;false&quot;>mapbox://styles/mapbox/traffic-day-v2&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/strings.xml"
- line="27"
- column="13"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.string.mapbox_style_traffic_night` appears to be unused"
- errorLine1=" &lt;string name=&quot;mapbox_style_traffic_night&quot; translatable=&quot;false&quot;>mapbox://styles/mapbox/traffic-night-v2&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/strings.xml"
- line="28"
- column="13"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.infowindow_subdescription` appears to be unused"
- errorLine1=" android:id=&quot;@+id/infowindow_subdescription&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/mapbox_infowindow_content.xml"
- line="43"
- column="9"/>
- </issue>
-
- <issue
id="TypographyQuotes"
message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?"
errorLine1=" &lt;string name=&quot;mapbox_attributionTelemetryMessage&quot;>Estàs ajudant a millorar els mapes d\&apos;OpenStreetMap i de Mapbox aportant dades d\&apos;ús anònimes.&lt;/string>"
@@ -554,76 +41,4 @@
column="55"/>
</issue>
- <issue
- id="IconDensities"
- message="Missing the following drawables in `drawable-hdpi`: mapbox_infowindow_icon_bg.9.png, mapbox_mapview_preview.jpg">
- <location
- file="src/main/res/drawable-hdpi"/>
- </issue>
-
- <issue
- id="IconDensities"
- message="Missing the following drawables in `drawable-mdpi`: mapbox_infowindow_icon_bg.9.png, mapbox_mapview_preview.jpg">
- <location
- file="src/main/res/drawable-mdpi"/>
- </issue>
-
- <issue
- id="IconDensities"
- message="Missing the following drawables in `drawable-xhdpi`: mapbox_infowindow_icon_bg.9.png, mapbox_mapview_preview.jpg">
- <location
- file="src/main/res/drawable-xhdpi"/>
- </issue>
-
- <issue
- id="IconDensities"
- message="Missing the following drawables in `drawable-xxhdpi`: mapbox_infowindow_icon_bg.9.png (found in drawable-xxxhdpi)">
- <location
- file="src/main/res/drawable-xxhdpi"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1="&lt;TextView"
- errorLine2="^">
- <location
- file="src/main/res/layout/mapbox_attribution_list_item.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/mapbox_infowindow_content.xml"
- line="21"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/mapbox_infowindow_content.xml"
- line="31"
- column="5"/>
- </issue>
-
- <issue
- id="ContentDescription"
- message="[Accessibility] Missing `contentDescription` attribute on image"
- errorLine1="&lt;ImageView xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
- errorLine2="^">
- <location
- file="src/main/res/layout/mapbox_view_image_marker.xml"
- line="2"
- column="1"/>
- </issue>
-
</issues>
diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
index 68edc12f35..b5a1d82c81 100644
--- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
+++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
@@ -7,4 +7,9 @@
-keep interface com.mapbox.mapboxsdk.** { *; }
-keep class com.mapbox.services.android.telemetry.** { *; }
-keep class com.mapbox.services.commons.** { *;}
--keep class com.google.gson.** { *; } \ No newline at end of file
+-keep class com.google.gson.** { *; }
+
+# config for okhttp 3.8.0, https://github.com/square/okhttp/pull/3354
+-dontwarn okio.**
+-dontwarn javax.annotation.Nullable
+-dontwarn javax.annotation.ParametersAreNonnullByDefault \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java
index 71fb9aa168..db492b6556 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java
@@ -54,8 +54,7 @@ public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
ViewConfiguration config = ViewConfiguration.get(context);
- // We divide edge slop by 2 to make rotation gesture happen more easily #6870
- edgeSlop = config.getScaledEdgeSlop() / 2;
+ edgeSlop = config.getScaledEdgeSlop();
}
@Override
@@ -222,4 +221,4 @@ public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
return focus.y;
}
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
index 3af7921596..7fd9d6172d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -15,6 +16,8 @@ import com.mapbox.services.android.telemetry.MapboxTelemetry;
import com.mapbox.services.android.telemetry.location.LocationEngine;
import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
+import timber.log.Timber;
+
/**
* The entry point to initialize the Mapbox Android SDK.
* <p>
@@ -26,6 +29,7 @@ import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
@UiThread
public final class Mapbox {
+ @SuppressLint("StaticFieldLeak")
private static Mapbox INSTANCE;
private Context context;
private String accessToken;
@@ -49,8 +53,14 @@ public final class Mapbox {
INSTANCE = new Mapbox(appContext, accessToken, new LocationSource(appContext));
LocationEngine locationEngine = new LocationSource(appContext);
locationEngine.setPriority(LocationEnginePriority.NO_POWER);
- MapboxTelemetry.getInstance().initialize(
- appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine);
+
+ try {
+ MapboxTelemetry.getInstance().initialize(
+ appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine);
+ } catch (Exception exception) {
+ Timber.e(exception, "Unable to instantiate Mapbox telemetry");
+ }
+
ConnectivityReceiver.instance(appContext);
}
return INSTANCE;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
index ddedf3debf..3fd2fa4ebf 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
@@ -14,7 +14,10 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
*
* @param <U> Type of the marker view to be composed.
* @param <T> Type of the builder to be used for composing.
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends BaseMarkerViewOptions<U, T>>
implements Parcelable {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
index f9ca9bf4cc..3c9cb31211 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.annotations;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -35,6 +36,7 @@ public final class IconFactory {
public static final String ICON_MARKERVIEW_ID = ICON_ID_PREFIX + "marker_view";
private Context context;
+ @SuppressLint("StaticFieldLeak")
private static IconFactory instance;
private Icon defaultMarker;
private Icon defaultMarkerView;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
index 56e8cc4ce2..eb82c7bf53 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
@@ -24,7 +24,10 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
* used with event listeners to bring up info windows. An {@link InfoWindow} is displayed by default
* when either a title or snippet is provided.
* </p>
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public class MarkerView extends Marker {
private MarkerViewManager markerViewManager;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
index 8704e882ea..8304d0e6ed 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
@@ -30,7 +30,10 @@ import java.util.Map;
* <p>
* This class is responsible for managing a {@link MarkerView} item.
* </p>
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public class MarkerViewManager implements MapView.OnMapChangedListener {
private final ViewGroup markerViewContainer;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
index 2d829537fc..79c72e5f70 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
@@ -12,7 +12,10 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
* <p>
* Do not extend this class directly but extend {@link BaseMarkerViewOptions} instead.
* </p>
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerViewOptions> {
private MarkerView marker;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
index 1ee59057d2..97a9ea94ee 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
@@ -55,7 +55,7 @@ public class MapboxConstants {
/**
* The currently supported maximum zoom level.
*/
- public static final float MAXIMUM_ZOOM = 20.0f;
+ public static final float MAXIMUM_ZOOM = 25.5f;
/**
* The currently supported maximum tilt value.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java
new file mode 100644
index 0000000000..7fc70716da
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java
@@ -0,0 +1,293 @@
+package com.mapbox.mapboxsdk.egl;
+
+import android.opengl.GLSurfaceView;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.utils.Compare.compare;
+import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_MASK_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_BUFFER_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_COLOR_BUFFER_TYPE;
+import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
+import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RGB_BUFFER;
+import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
+import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
+import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
+import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
+
+/**
+ * Selects the right EGLConfig needed for `mapbox-gl-native`
+ */
+public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
+
+ /**
+ * Requires API level 17
+ *
+ * @see android.opengl.EGL14.EGL_CONFORMANT;
+ */
+ @SuppressWarnings("JavadocReference")
+ private static final int EGL_CONFORMANT = 0x3042;
+
+ /**
+ * Requires API level 17
+ *
+ * @see android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
+ */
+ @SuppressWarnings("JavadocReference")
+ private static final int EGL_OPENGL_ES2_BIT = 0x0004;
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] configAttribs = getConfigAttributes();
+
+ // Determine number of possible configurations
+ int[] numConfigs = getNumberOfConfigurations(egl, display, configAttribs);
+ if (numConfigs[0] < 1) {
+ Timber.e("eglChooseConfig() returned no configs.");
+ throw new EGLConfigException("eglChooseConfig() failed");
+ }
+
+ // Get all possible configurations
+ EGLConfig[] possibleConfigurations = getPossibleConfigurations(egl, display, configAttribs, numConfigs);
+
+ // Choose best match
+ EGLConfig config = chooseBestMatchConfig(egl, display, possibleConfigurations);
+ if (config == null) {
+ Timber.e("No config chosen");
+ throw new EGLConfigException("No config chosen");
+ }
+
+ return config;
+ }
+
+ private int[] getNumberOfConfigurations(EGL10 egl, EGLDisplay display, int[] configAttributes) {
+ int[] numConfigs = new int[1];
+ if (!egl.eglChooseConfig(display, configAttributes, null, 0, numConfigs)) {
+ Timber.e("eglChooseConfig(NULL) returned error %d", egl.eglGetError());
+ throw new EGLConfigException("eglChooseConfig() failed");
+ }
+ return numConfigs;
+ }
+
+ private EGLConfig[] getPossibleConfigurations(EGL10 egl, EGLDisplay display,
+ int[] configAttributes, int[] numConfigs) {
+ EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+ if (!egl.eglChooseConfig(display, configAttributes, configs, numConfigs[0], numConfigs)) {
+ Timber.e("eglChooseConfig() returned error %d", egl.eglGetError());
+ throw new EGLConfigException("eglChooseConfig() failed");
+ }
+ return configs;
+ }
+
+ // Quality
+ enum BufferFormat {
+ Format16Bit(3),
+ Format32BitNoAlpha(1),
+ Format32BitAlpha(2),
+ Format24Bit(0),
+ Unknown(4);
+
+ int value;
+
+ BufferFormat(int value) {
+ this.value = value;
+ }
+ }
+
+ enum DepthStencilFormat {
+ Format16Depth8Stencil(1),
+ Format24Depth8Stencil(0);
+
+ int value;
+
+ DepthStencilFormat(int value) {
+ this.value = value;
+ }
+ }
+
+ private EGLConfig chooseBestMatchConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
+ class Config implements Comparable<Config> {
+ private final BufferFormat bufferFormat;
+ private final DepthStencilFormat depthStencilFormat;
+ private final boolean isNotConformant;
+ private final boolean isCaveat;
+ private final int index;
+ private final EGLConfig config;
+
+ public Config(BufferFormat bufferFormat, DepthStencilFormat depthStencilFormat,
+ boolean isNotConformant, boolean isCaveat, int index, EGLConfig config) {
+ this.bufferFormat = bufferFormat;
+ this.depthStencilFormat = depthStencilFormat;
+ this.isNotConformant = isNotConformant;
+ this.isCaveat = isCaveat;
+ this.index = index;
+ this.config = config;
+ }
+
+
+ @Override
+ public int compareTo(@NonNull Config other) {
+ int i = compare(bufferFormat.value, other.bufferFormat.value);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(depthStencilFormat.value, other.depthStencilFormat.value);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(isNotConformant, other.isNotConformant);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(isCaveat, other.isCaveat);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(index, other.index);
+ if (i != 0) {
+ return i;
+ }
+
+ return 0;
+ }
+ }
+
+ List<Config> matches = new ArrayList<>();
+
+ int i = 0;
+ for (EGLConfig config : configs) {
+ i++;
+
+ int caveat = getConfigAttr(egl, display, config, EGL_CONFIG_CAVEAT);
+ int conformant = getConfigAttr(egl, display, config, EGL_CONFORMANT);
+ int bits = getConfigAttr(egl, display, config, EGL_BUFFER_SIZE);
+ int red = getConfigAttr(egl, display, config, EGL_RED_SIZE);
+ int green = getConfigAttr(egl, display, config, EGL_GREEN_SIZE);
+ int blue = getConfigAttr(egl, display, config, EGL_BLUE_SIZE);
+ int alpha = getConfigAttr(egl, display, config, EGL_ALPHA_SIZE);
+ int alphaMask = getConfigAttr(egl, display, config, EGL_ALPHA_MASK_SIZE);
+ int depth = getConfigAttr(egl, display, config, EGL_DEPTH_SIZE);
+ int stencil = getConfigAttr(egl, display, config, EGL_STENCIL_SIZE);
+ int sampleBuffers = getConfigAttr(egl, display, config, EGL_SAMPLE_BUFFERS);
+ int samples = getConfigAttr(egl, display, config, EGL_SAMPLES);
+
+ boolean configOk = (depth == 24) || (depth == 16);
+ configOk &= stencil == 8;
+ configOk &= sampleBuffers == 0;
+ configOk &= samples == 0;
+
+ // Filter our configs first for depth, stencil and anti-aliasing
+ if (configOk) {
+ // Work out the config's buffer format
+ BufferFormat bufferFormat;
+ if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) {
+ bufferFormat = BufferFormat.Format16Bit;
+ } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
+ bufferFormat = BufferFormat.Format32BitNoAlpha;
+ } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
+ bufferFormat = BufferFormat.Format32BitAlpha;
+ } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
+ bufferFormat = BufferFormat.Format24Bit;
+ } else {
+ bufferFormat = BufferFormat.Unknown;
+ }
+
+ // Work out the config's depth stencil format
+ DepthStencilFormat depthStencilFormat;
+ if ((depth == 16) && (stencil == 8)) {
+ depthStencilFormat = DepthStencilFormat.Format16Depth8Stencil;
+ } else {
+ depthStencilFormat = DepthStencilFormat.Format24Depth8Stencil;
+ }
+
+ boolean isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
+ boolean isCaveat = caveat != EGL_NONE;
+
+ // Ignore formats we don't recognise
+ if (bufferFormat != BufferFormat.Unknown) {
+ matches.add(new Config(bufferFormat, depthStencilFormat, isNotConformant, isCaveat, i, config));
+ }
+ }
+
+ }
+
+ // Sort
+ Collections.sort(matches);
+
+ if (matches.size() == 0) {
+ throw new EGLConfigException("No matching configurations after filtering");
+ }
+
+ Config bestMatch = matches.get(0);
+
+ if (bestMatch.isCaveat) {
+ Timber.w("Chosen config has a caveat.");
+ }
+
+ if (bestMatch.isNotConformant) {
+ Timber.w("Chosen config is not conformant.");
+ }
+
+ return bestMatch.config;
+ }
+
+ private int getConfigAttr(EGL10 egl, EGLDisplay display, EGLConfig config, int attributeName) {
+ int[] attributevalue = new int[1];
+ if (!egl.eglGetConfigAttrib(display, config, attributeName, attributevalue)) {
+ Timber.e("eglGetConfigAttrib(%d) returned error %d", attributeName, egl.eglGetError());
+ throw new EGLConfigException("eglGetConfigAttrib() failed");
+ }
+ return attributevalue[0];
+ }
+
+
+ private int[] getConfigAttributes() {
+ boolean emulator = inEmulator();
+ Timber.i("In emulator: %s", emulator);
+
+ // Get all configs at least RGB 565 with 16 depth and 8 stencil
+ return new int[] {
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_BUFFER_SIZE, 16,
+ EGL_RED_SIZE, 5,
+ EGL_GREEN_SIZE, 6,
+ EGL_BLUE_SIZE, 5,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, 8,
+ (emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT,
+ (emulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+ }
+
+ /**
+ * Detect if we are in emulator.
+ */
+ private boolean inEmulator() {
+ return System.getProperty("ro.kernel.qemu") != null;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java
new file mode 100644
index 0000000000..3f576d0eda
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java
@@ -0,0 +1,21 @@
+package com.mapbox.mapboxsdk.egl;
+
+/**
+ * Used for EGL configuration exceptions
+ */
+public class EGLConfigException extends RuntimeException {
+ public EGLConfigException() {
+ }
+
+ public EGLConfigException(String message) {
+ super(message);
+ }
+
+ public EGLConfigException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EGLConfigException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
index 505b2db192..4fcb91033c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
@@ -222,6 +222,16 @@ public class LatLngBounds implements Parcelable {
}
/**
+ * Constructs a LatLngBounds from doubles representing a LatLng pair.
+ * <p>
+ * This method doesn't recalculate most east or most west boundaries.
+ * </p>
+ */
+ public static LatLngBounds from(double latNorth, double lonEast, double latSouth, double lonWest) {
+ return new LatLngBounds(latNorth, lonEast, latSouth, lonWest);
+ }
+
+ /**
* Constructs a LatLngBounds from current bounds with an additional latitude-longitude pair.
*
* @param latLng the latitude lognitude pair to include in the bounds.
@@ -440,7 +450,7 @@ public class LatLngBounds implements Parcelable {
*/
public Builder includes(List<LatLng> latLngs) {
for (LatLng point : latLngs) {
- latLngList.add(point);
+ include(point);
}
return this;
}
@@ -452,7 +462,9 @@ public class LatLngBounds implements Parcelable {
* @return this
*/
public Builder include(@NonNull LatLng latLng) {
- latLngList.add(latLng);
+ if (!latLngList.contains(latLng)) {
+ latLngList.add(latLng);
+ }
return this;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
index 9c8cda5544..e2626a026b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
@@ -56,13 +56,14 @@ class HTTPRequest implements Callback {
mNativePtr = nativePtr;
try {
- // Don't try a request if we aren't connected
- if (!Mapbox.isConnected()) {
+ HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
+ final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
+
+ // Don't try a request to remote server if we aren't connected
+ if (!Mapbox.isConnected() && !host.equals("127.0.0.1") && !host.equals("localhost")) {
throw new NoRouteToHostException("No Internet connection available.");
}
- HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
- final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
if (host.equals("mapbox.com") || host.endsWith(".mapbox.com") || host.equals("mapbox.cn")
|| host.endsWith(".mapbox.cn")) {
if (httpUrl.querySize() == 0) {
@@ -109,13 +110,11 @@ class HTTPRequest implements Callback {
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
- Timber.v(String.format("[HTTP] Request was successful (code = %d).", response.code()));
+ Timber.v("[HTTP] Request was successful (code = %s).", response.code());
} else {
// We don't want to call this unsuccessful because a 304 isn't really an error
String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information";
- Timber.d(String.format(
- "[HTTP] Request with response code = %d: %s",
- response.code(), message));
+ Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message);
}
byte[] body;
@@ -160,15 +159,12 @@ class HTTPRequest implements Callback {
String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request";
if (type == TEMPORARY_ERROR) {
- Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Request failed due to a temporary error: %s", errorMessage));
+ Timber.d("Request failed due to a temporary error: %s", errorMessage);
} else if (type == CONNECTION_ERROR) {
- Timber.i(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Request failed due to a connection error: %s", errorMessage));
+ Timber.i("Request failed due to a connection error: %s", errorMessage);
} else {
// PERMANENT_ERROR
- Timber.w(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Request failed due to a permanent error: %s", errorMessage));
+ Timber.w("Request failed due to a permanent error: %s", errorMessage);
}
mLock.lock();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
index edf448ab4f..c09c926eb5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
@@ -118,6 +118,9 @@ class AnnotationManager {
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ // do icon cleanup
+ iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
@@ -137,6 +140,8 @@ class AnnotationManager {
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
@@ -159,6 +164,8 @@ class AnnotationManager {
marker.hideInfoWindow();
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ iconManager.iconCleanup(marker.getIcon());
}
} else {
// instanceOf Polygon/Polyline
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
index 18eecfd9c3..b1d6df2103 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
@@ -7,10 +7,10 @@ import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerView;
-import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException;
-import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Responsible for managing icons added to the Map.
@@ -25,15 +25,14 @@ import java.util.List;
*/
class IconManager {
- private NativeMapView nativeMapView;
- private List<Icon> icons;
+ private final Map<Icon, Integer> iconMap = new HashMap<>();
+ private NativeMapView nativeMapView;
private int highestIconWidth;
private int highestIconHeight;
IconManager(NativeMapView nativeMapView) {
this.nativeMapView = nativeMapView;
- this.icons = new ArrayList<>();
// load transparent icon for MarkerView to trace actual markers, see #6352
loadIcon(IconFactory.recreate(IconFactory.ICON_MARKERVIEW_ID, IconFactory.ICON_MARKERVIEW_BITMAP));
}
@@ -83,13 +82,13 @@ class IconManager {
}
private void addIcon(Icon icon, boolean addIconToMap) {
- if (!icons.contains(icon)) {
- icons.add(icon);
+ if (!iconMap.keySet().contains(icon)) {
+ iconMap.put(icon, 1);
if (addIconToMap) {
loadIcon(icon);
}
} else {
- validateIconChanged(icon);
+ iconMap.put(icon, iconMap.get(icon) + 1);
}
}
@@ -121,18 +120,11 @@ class IconManager {
}
void reloadIcons() {
- for (Icon icon : icons) {
+ for (Icon icon : iconMap.keySet()) {
loadIcon(icon);
}
}
- private void validateIconChanged(Icon icon) {
- Icon oldIcon = icons.get(icons.indexOf(icon));
- if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) {
- throw new IconBitmapChangedException();
- }
- }
-
void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) {
Icon icon = marker.getIcon();
if (icon == null) {
@@ -149,4 +141,23 @@ class IconManager {
marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon));
}
}
+
+ void iconCleanup(Icon icon) {
+ int refCounter = iconMap.get(icon) - 1;
+ if (refCounter == 0) {
+ remove(icon);
+ } else {
+ updateIconRefCounter(icon, refCounter);
+ }
+ }
+
+ private void remove(Icon icon) {
+ nativeMapView.removeAnnotationIcon(icon.getId());
+ iconMap.remove(icon);
+ }
+
+ private void updateIconRefCounter(Icon icon, int refCounter) {
+ iconMap.put(icon, refCounter);
+ }
+
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
index f456d3ef65..2394e52193 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
@@ -54,8 +54,10 @@ final class MapGestureDetector {
private boolean quickZoom;
private boolean tiltGestureOccurred;
private boolean scrollGestureOccurred;
+
private boolean scaleGestureOccurred;
private boolean recentScaleGestureOccurred;
+ private long scaleBeginTime;
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
TrackingSettings trackingSettings, AnnotationManager annotationManager,
@@ -144,8 +146,8 @@ final class MapGestureDetector {
}
// Check two finger gestures first
- rotateGestureDetector.onTouchEvent(event);
scaleGestureDetector.onTouchEvent(event);
+ rotateGestureDetector.onTouchEvent(event);
shoveGestureDetector.onTouchEvent(event);
// Handle two finger tap
@@ -428,8 +430,7 @@ final class MapGestureDetector {
*/
private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
- long beginTime = 0;
- float scaleFactor = 1.0f;
+ private float scaleFactor = 1.0f;
// Called when two fingers first touch the screen
@Override
@@ -438,9 +439,8 @@ final class MapGestureDetector {
return false;
}
- scaleGestureOccurred = true;
recentScaleGestureOccurred = true;
- beginTime = detector.getEventTime();
+ scaleBeginTime = detector.getEventTime();
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
MapboxEvent.GESTURE_PINCH_START, transform));
@@ -451,7 +451,7 @@ final class MapGestureDetector {
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
scaleGestureOccurred = false;
- beginTime = 0;
+ scaleBeginTime = 0;
scaleFactor = 1.0f;
cameraChangeDispatcher.onCameraIdle();
}
@@ -471,7 +471,7 @@ final class MapGestureDetector {
// Ignore short touches in case it is a tap
// Also ignore small scales
long time = detector.getEventTime();
- long interval = time - beginTime;
+ long interval = time - scaleBeginTime;
if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
return false;
}
@@ -517,7 +517,6 @@ final class MapGestureDetector {
transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2),
detector.getFocusX(), detector.getFocusY());
}
-
return true;
}
}
@@ -527,9 +526,11 @@ final class MapGestureDetector {
*/
private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
- long beginTime = 0;
- float totalAngle = 0.0f;
- boolean started = false;
+ private static final long ROTATE_INVOKE_WAIT_TIME = 1500;
+
+ private long beginTime = 0;
+ private float totalAngle = 0.0f;
+ private boolean started = false;
// Called when two fingers first touch the screen
@Override
@@ -566,7 +567,7 @@ final class MapGestureDetector {
// Also ignore small rotate
long time = detector.getEventTime();
long interval = time - beginTime;
- if (!started && (interval <= ViewConfiguration.getTapTimeout())) {
+ if (!started && (interval <= ViewConfiguration.getTapTimeout() || isScaleGestureActive(time))) {
return false;
}
@@ -583,6 +584,7 @@ final class MapGestureDetector {
if (!started) {
return false;
}
+
// rotation constitutes translation of anything except the center of
// rotation, so cancel both location and bearing tracking if required
trackingSettings.resetTrackingModesIfRequired(true, true, false);
@@ -601,6 +603,13 @@ final class MapGestureDetector {
}
return true;
}
+
+ private boolean isScaleGestureActive(long time) {
+ long scaleExecutionTime = time - scaleBeginTime;
+ boolean scaleGestureStarted = scaleBeginTime != 0;
+ boolean scaleOffsetTimeValid = scaleExecutionTime > ROTATE_INVOKE_WAIT_TIME;
+ return (scaleGestureStarted && scaleOffsetTimeValid) || scaleGestureOccurred;
+ }
}
/**
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 848a898f94..12e4c675ee 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -1,10 +1,9 @@
package com.mapbox.mapboxsdk.maps;
import android.content.Context;
-import android.graphics.Canvas;
import android.graphics.PointF;
-import android.graphics.SurfaceTexture;
import android.os.Build;
+import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.IntDef;
@@ -16,10 +15,6 @@ import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -32,6 +27,8 @@ import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.egl.EGLConfigChooser;
+import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
@@ -45,8 +42,15 @@ import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
import timber.log.Timber;
+import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION;
+import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE;
+import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY;
+
/**
* <p>
* A {@code MapView} provides an embeddable map interface.
@@ -69,10 +73,10 @@ public class MapView extends FrameLayout {
private NativeMapView nativeMapView;
private MapboxMapOptions mapboxMapOptions;
private boolean destroyed;
- private boolean hasSurface;
private MyLocationView myLocationView;
private CompassView compassView;
+ private PointF focalPoint;
private ImageView attrView;
private ImageView logoView;
@@ -82,6 +86,8 @@ public class MapView extends FrameLayout {
private Bundle savedInstanceState;
private final CopyOnWriteArrayList<OnMapChangedListener> onMapChangedListeners = new CopyOnWriteArrayList<>();
+ private GLSurfaceView glSurfaceView;
+
@UiThread
public MapView(@NonNull Context context) {
super(context);
@@ -108,8 +114,7 @@ public class MapView extends FrameLayout {
private void initialise(@NonNull final Context context, @NonNull final MapboxMapOptions options) {
if (isInEditMode()) {
- // in IDE, show preview map
- LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_preview, this);
+ // in IDE layout editor, just return
return;
}
mapboxMapOptions = options;
@@ -133,7 +138,7 @@ public class MapView extends FrameLayout {
} else {
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
- initialiseDrawingSurface(mapboxMapOptions);
+ initialiseDrawingSurface();
}
});
}
@@ -143,7 +148,8 @@ public class MapView extends FrameLayout {
addOnMapChangedListener(mapCallback);
// callback for focal point invalidation
- FocalPointInvalidator focalPoint = new FocalPointInvalidator(compassView);
+ final FocalPointInvalidator focalPointInvalidator = new FocalPointInvalidator();
+ focalPointInvalidator.addListener(createFocalPointChangeListener());
// callback for registering touch listeners
RegisterTouchListener registerTouchListener = new RegisterTouchListener();
@@ -152,13 +158,15 @@ public class MapView extends FrameLayout {
CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator();
// callback for camera change events
- CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher();
+ final CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher();
// setup components for MapboxMap creation
Projection proj = new Projection(nativeMapView);
- UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, logoView);
- TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator);
- MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint);
+ UiSettings uiSettings = new UiSettings(proj, focalPointInvalidator, compassView, attrView, logoView);
+ TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointInvalidator,
+ zoomInvalidator);
+ MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj,
+ focalPointInvalidator);
LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>();
MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer));
IconManager iconManager = new IconManager(nativeMapView);
@@ -170,8 +178,11 @@ public class MapView extends FrameLayout {
markerViewManager, iconManager, annotations, markers, polygons, polylines);
Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings,
cameraChangeDispatcher);
+
mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj,
registerTouchListener, annotationManager, cameraChangeDispatcher);
+ focalPointInvalidator.addListener(mapboxMap.createFocalPointChangeListener());
+
mapCallback.attachMapboxMap(mapboxMap);
// user input
@@ -179,11 +190,13 @@ public class MapView extends FrameLayout {
annotationManager, cameraChangeDispatcher);
mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
+ mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this));
MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform);
- mapZoomButtonController = new MapZoomButtonController(this, uiSettings, zoomListener);
+ mapZoomButtonController.bind(uiSettings, zoomListener);
+ compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher));
+ compassView.setOnClickListener(createCompassClickListener(cameraChangeDispatcher));
// inject widgets with MapboxMap
- compassView.setMapboxMap(mapboxMap);
myLocationView.setMapboxMap(mapboxMap);
attrView.setOnClickListener(new AttributionDialogManager(context, mapboxMap));
@@ -205,6 +218,49 @@ public class MapView extends FrameLayout {
}
}
+ private FocalPointChangeListener createFocalPointChangeListener() {
+ return new FocalPointChangeListener() {
+ @Override
+ public void onFocalPointChanged(PointF pointF) {
+ focalPoint = pointF;
+ }
+ };
+ }
+
+ private MapboxMap.OnCompassAnimationListener createCompassAnimationListener(final CameraChangeDispatcher
+ cameraChangeDispatcher) {
+ return new MapboxMap.OnCompassAnimationListener() {
+ @Override
+ public void onCompassAnimation() {
+ cameraChangeDispatcher.onCameraMove();
+ }
+
+ @Override
+ public void onCompassAnimationFinished() {
+ compassView.isAnimating(false);
+ cameraChangeDispatcher.onCameraIdle();
+ }
+ };
+ }
+
+ private OnClickListener createCompassClickListener(final CameraChangeDispatcher cameraChangeDispatcher) {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mapboxMap != null && compassView != null) {
+ if (focalPoint != null) {
+ mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION);
+ } else {
+ mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION);
+ }
+ cameraChangeDispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION);
+ compassView.isAnimating(true);
+ compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION);
+ }
+ }
+ };
+ }
+
//
// Lifecycle events
//
@@ -229,17 +285,36 @@ public class MapView extends FrameLayout {
}
}
- private void initialiseDrawingSurface(MapboxMapOptions mapboxMapOptions) {
- if (mapboxMapOptions.getTextureMode()) {
- TextureView textureView = new TextureView(getContext());
- textureView.setSurfaceTextureListener(new SurfaceTextureListener());
- addView(textureView, 0);
- } else {
- SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
- surfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
- surfaceView.getHolder().addCallback(new SurfaceCallback());
- surfaceView.setVisibility(View.VISIBLE);
- }
+ private void initialiseDrawingSurface() {
+ glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView);
+ glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
+ glSurfaceView.setEGLContextClientVersion(2);
+ glSurfaceView.setEGLConfigChooser(new EGLConfigChooser());
+
+ MapRenderer mapRenderer = new MapRenderer(getContext(), glSurfaceView) {
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ MapView.this.post(new Runnable() {
+ @Override
+ public void run() {
+ // Initialise only once
+ if (mapboxMap == null) {
+ initialiseMap();
+ mapboxMap.onStart();
+ }
+ }
+ });
+
+ super.onSurfaceCreated(gl, config);
+ }
+ };
+
+ glSurfaceView.setRenderer(mapRenderer);
+ glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
+ glSurfaceView.setVisibility(View.VISIBLE);
+
+ nativeMapView = new NativeMapView(this, mapRenderer);
+ nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight());
}
/**
@@ -270,7 +345,9 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onResume() {
- // replaced by onStart in v5.0.0
+ if (glSurfaceView != null) {
+ glSurfaceView.onResume();
+ }
}
/**
@@ -278,7 +355,9 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onPause() {
- // replaced by onStop in v5.0.0
+ if (glSurfaceView != null) {
+ glSurfaceView.onPause();
+ }
}
/**
@@ -296,6 +375,7 @@ public class MapView extends FrameLayout {
@UiThread
public void onDestroy() {
destroyed = true;
+ mapCallback.clearOnMapReadyCallbacks();
nativeMapView.destroy();
nativeMapView = null;
}
@@ -359,21 +439,6 @@ public class MapView extends FrameLayout {
nativeMapView.onLowMemory();
}
- // Called when debug mode is enabled to update a FPS counter
- // Called via JNI from NativeMapView
- // Forward to any listener
- protected void onFpsChanged(final double fps) {
- final MapboxMap.OnFpsChangedListener listener = mapboxMap.getOnFpsChangedListener();
- if (listener != null) {
- post(new Runnable() {
- @Override
- public void run() {
- listener.onFpsChanged(fps);
- }
- });
- }
- }
-
/**
* <p>
* Loads a new map style from the specified URL.
@@ -417,29 +482,6 @@ public class MapView extends FrameLayout {
// Rendering
//
- // Called when the map needs to be rerendered
- // Called via JNI from NativeMapView
- protected void onInvalidate() {
- postInvalidate();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (isInEditMode()) {
- return;
- }
-
- if (destroyed) {
- return;
- }
-
- if (!hasSurface) {
- return;
- }
- nativeMapView.render();
- }
-
@Override
protected void onSizeChanged(int width, int height, int oldw, int oldh) {
if (destroyed) {
@@ -451,103 +493,6 @@ public class MapView extends FrameLayout {
}
}
- private class SurfaceCallback implements SurfaceHolder.Callback {
-
- private Surface surface;
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- if (nativeMapView == null) {
- nativeMapView = new NativeMapView(MapView.this);
- nativeMapView.createSurface(surface = holder.getSurface());
- nativeMapView.resizeView(getWidth(), getHeight());
- initialiseMap();
- mapboxMap.onStart();
- } else {
- nativeMapView.createSurface(surface = holder.getSurface());
- }
-
- hasSurface = true;
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- if (destroyed) {
- return;
- }
- nativeMapView.resizeFramebuffer(width, height);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- hasSurface = false;
-
- if (nativeMapView != null) {
- // occurs when activity goes to background
- nativeMapView.destroySurface();
- }
- surface.release();
- }
- }
-
- // This class handles TextureView callbacks
- private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
-
- private Surface surface;
-
- // Called when the native surface texture has been created
- // Must do all EGL/GL ES initialization here
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- if (nativeMapView == null) {
- nativeMapView = new NativeMapView(MapView.this);
- nativeMapView.createSurface(this.surface = new Surface(surface));
- nativeMapView.resizeFramebuffer(width, height);
- nativeMapView.resizeView(width, height);
- initialiseMap();
- mapboxMap.onStart();
- } else {
- nativeMapView.createSurface(this.surface = new Surface(surface));
- }
-
- hasSurface = true;
- }
-
- // Called when the native surface texture has been destroyed
- // Must do all EGL/GL ES destruction here
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- hasSurface = false;
-
- if (nativeMapView != null) {
- nativeMapView.destroySurface();
- }
- this.surface.release();
- return true;
- }
-
- // Called when the format or size of the native surface texture has been changed
- // Must handle window resizing here.
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- if (destroyed) {
- return;
- }
-
- nativeMapView.resizeFramebuffer(width, height);
- }
-
- // Called when the SurfaceTexure frame is drawn to screen
- // Must sync with UI here
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- if (destroyed) {
- return;
- }
- mapboxMap.onUpdateRegionChange();
- }
- }
-
//
// View events
//
@@ -557,9 +502,7 @@ public class MapView extends FrameLayout {
@CallSuper
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mapZoomButtonController != null) {
- mapZoomButtonController.setVisible(false);
- }
+ mapZoomButtonController.setVisible(false);
}
// Called when view is hidden and shown
@@ -569,7 +512,7 @@ public class MapView extends FrameLayout {
return;
}
- if (mapZoomButtonController != null && nativeMapView != null) {
+ if (mapZoomButtonController != null) {
mapZoomButtonController.setVisible(visibility == View.VISIBLE);
}
}
@@ -583,7 +526,7 @@ public class MapView extends FrameLayout {
try {
onMapChangedListener.onMapChanged(rawChange);
} catch (RuntimeException err) {
- Timber.e("Exception (%s) in MapView.OnMapChangedListener: %s", err.getClass(), err.getMessage());
+ Timber.e(err, "Exception in MapView.OnMapChangedListener");
}
}
}
@@ -878,10 +821,10 @@ public class MapView extends FrameLayout {
private class FocalPointInvalidator implements FocalPointChangeListener {
- private final FocalPointChangeListener[] focalPointChangeListeners;
+ private final List<FocalPointChangeListener> focalPointChangeListeners = new ArrayList<>();
- FocalPointInvalidator(FocalPointChangeListener... listeners) {
- focalPointChangeListeners = listeners;
+ void addListener(FocalPointChangeListener focalPointChangeListener) {
+ focalPointChangeListeners.add(focalPointChangeListener);
}
@Override
@@ -1016,5 +959,9 @@ public class MapView extends FrameLayout {
void addOnMapReadyCallback(OnMapReadyCallback callback) {
onMapReadyCallbackList.add(callback);
}
+
+ void clearOnMapReadyCallbacks() {
+ onMapReadyCallbackList.clear();
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java
index 16513904c5..018c8eb5bb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.maps;
import android.support.annotation.NonNull;
-import android.view.View;
import android.widget.ZoomButtonsController;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
@@ -12,21 +11,25 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants;
* Allows single touch only devices to zoom in and out.
* </p>
*/
-final class MapZoomButtonController extends ZoomButtonsController {
+final class MapZoomButtonController {
private UiSettings uiSettings;
+ private ZoomButtonsController zoomButtonsController;
- MapZoomButtonController(@NonNull View ownerView, @NonNull UiSettings uiSettings, @NonNull OnZoomListener listener) {
- super(ownerView);
+ MapZoomButtonController(@NonNull ZoomButtonsController zoomButtonsController) {
+ this.zoomButtonsController = zoomButtonsController;
+ this.zoomButtonsController.setZoomSpeed(MapboxConstants.ANIMATION_DURATION);
+ }
+
+ void bind(UiSettings uiSettings, ZoomButtonsController.OnZoomListener onZoomListener) {
this.uiSettings = uiSettings;
- setZoomSpeed(MapboxConstants.ANIMATION_DURATION);
- setOnZoomListener(listener);
+ zoomButtonsController.setOnZoomListener(onZoomListener);
}
- @Override
- public void setVisible(boolean visible) {
- if (uiSettings.isZoomControlsEnabled()) {
- super.setVisible(visible);
+ void setVisible(boolean visible) {
+ if (uiSettings != null && !uiSettings.isZoomControlsEnabled()) {
+ return;
}
+ zoomButtonsController.setVisible(visible);
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index f47fcdfd4a..c417b3fc26 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
@@ -44,6 +44,7 @@ import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.services.android.telemetry.location.LocationEngine;
import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.Geometry;
import java.lang.reflect.ParameterizedType;
import java.util.List;
@@ -59,6 +60,7 @@ import timber.log.Timber;
* Note: Similar to a View object, a MapboxMap should only be read and modified from the main thread.
* </p>
*/
+@UiThread
public final class MapboxMap {
private final NativeMapView nativeMapView;
@@ -74,6 +76,7 @@ public final class MapboxMap {
private final OnRegisterTouchListener onRegisterTouchListener;
private MapboxMap.OnFpsChangedListener onFpsChangedListener;
+ private PointF focalPoint;
MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking,
MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener,
@@ -210,7 +213,6 @@ public final class MapboxMap {
*
* @return Duration in milliseconds
*/
- @UiThread
public long getTransitionDuration() {
return nativeMapView.getTransitionDuration();
}
@@ -220,7 +222,6 @@ public final class MapboxMap {
*
* @param durationMs Duration in milliseconds
*/
- @UiThread
public void setTransitionDuration(long durationMs) {
nativeMapView.setTransitionDuration(durationMs);
}
@@ -233,7 +234,6 @@ public final class MapboxMap {
*
* @return Delay in milliseconds
*/
- @UiThread
public long getTransitionDelay() {
return nativeMapView.getTransitionDelay();
}
@@ -243,7 +243,6 @@ public final class MapboxMap {
*
* @param delayMs Delay in milliseconds
*/
- @UiThread
public void setTransitionDelay(long delayMs) {
nativeMapView.setTransitionDelay(delayMs);
}
@@ -263,7 +262,6 @@ public final class MapboxMap {
*
* @param enable true to enable
*/
- @UiThread
public void setPrefetchesTiles(boolean enable) {
nativeMapView.setPrefetchesTiles(enable);
}
@@ -272,10 +270,8 @@ public final class MapboxMap {
* Check whether tile pre-fetching is enabled or not.
*
* @return true if enabled
- *
* @see MapboxMap#setPrefetchesTiles(boolean)
*/
- @UiThread
public boolean getPrefetchesTiles() {
return nativeMapView.getPrefetchesTiles();
}
@@ -285,7 +281,6 @@ public final class MapboxMap {
*
* @return all the layers in the current style
*/
- @UiThread
public List<Layer> getLayers() {
return nativeMapView.getLayers();
}
@@ -297,7 +292,6 @@ public final class MapboxMap {
* @return the layer, if present in the style
*/
@Nullable
- @UiThread
public Layer getLayer(@NonNull String layerId) {
return nativeMapView.getLayer(layerId);
}
@@ -310,13 +304,12 @@ public final class MapboxMap {
* @return the casted Layer, null if another type
*/
@Nullable
- @UiThread
public <T extends Layer> T getLayerAs(@NonNull String layerId) {
try {
// noinspection unchecked
return (T) nativeMapView.getLayer(layerId);
} catch (ClassCastException exception) {
- Timber.e(String.format("Layer: %s is a different type: %s", layerId, exception));
+ Timber.e(exception, "Layer: %s is a different type: ", layerId);
return null;
}
}
@@ -326,7 +319,6 @@ public final class MapboxMap {
*
* @param layer the layer to add
*/
- @UiThread
public void addLayer(@NonNull Layer layer) {
nativeMapView.addLayer(layer);
}
@@ -337,7 +329,6 @@ public final class MapboxMap {
* @param layer the layer to add
* @param below the layer id to add this layer before
*/
- @UiThread
public void addLayerBelow(@NonNull Layer layer, @NonNull String below) {
nativeMapView.addLayerBelow(layer, below);
}
@@ -348,7 +339,6 @@ public final class MapboxMap {
* @param layer the layer to add
* @param above the layer id to add this layer above
*/
- @UiThread
public void addLayerAbove(@NonNull Layer layer, @NonNull String above) {
nativeMapView.addLayerAbove(layer, above);
}
@@ -360,7 +350,6 @@ public final class MapboxMap {
* @param layer the layer to add
* @param index the index to insert the layer at
*/
- @UiThread
public void addLayerAt(@NonNull Layer layer, @IntRange(from = 0) int index) {
nativeMapView.addLayerAt(layer, index);
}
@@ -371,7 +360,6 @@ public final class MapboxMap {
* @param layerId the layer to remove
* @return the removed layer or null if not found
*/
- @UiThread
@Nullable
public Layer removeLayer(@NonNull String layerId) {
return nativeMapView.removeLayer(layerId);
@@ -383,7 +371,6 @@ public final class MapboxMap {
* @param layer the layer to remove
* @return the layer
*/
- @UiThread
@Nullable
public Layer removeLayer(@NonNull Layer layer) {
return nativeMapView.removeLayer(layer);
@@ -395,7 +382,6 @@ public final class MapboxMap {
* @param index the layer index
* @return the removed layer or null if not found
*/
- @UiThread
@Nullable
public Layer removeLayerAt(@IntRange(from = 0) int index) {
return nativeMapView.removeLayerAt(index);
@@ -406,7 +392,6 @@ public final class MapboxMap {
*
* @return all the sources in the current style
*/
- @UiThread
public List<Source> getSources() {
return nativeMapView.getSources();
}
@@ -418,7 +403,6 @@ public final class MapboxMap {
* @return the source if present in the current style
*/
@Nullable
- @UiThread
public Source getSource(@NonNull String sourceId) {
return nativeMapView.getSource(sourceId);
}
@@ -431,13 +415,12 @@ public final class MapboxMap {
* @return the casted Source, null if another type
*/
@Nullable
- @UiThread
public <T extends Source> T getSourceAs(@NonNull String sourceId) {
try {
// noinspection unchecked
return (T) nativeMapView.getSource(sourceId);
} catch (ClassCastException exception) {
- Timber.e(String.format("Source: %s is a different type: %s", sourceId, exception));
+ Timber.e(exception, "Source: %s is a different type: ", sourceId);
return null;
}
}
@@ -447,7 +430,6 @@ public final class MapboxMap {
*
* @param source the source to add
*/
- @UiThread
public void addSource(@NonNull Source source) {
nativeMapView.addSource(source);
}
@@ -458,7 +440,6 @@ public final class MapboxMap {
* @param sourceId the source to remove
* @return the source handle or null if the source was not present
*/
- @UiThread
@Nullable
public Source removeSource(@NonNull String sourceId) {
return nativeMapView.removeSource(sourceId);
@@ -470,7 +451,6 @@ public final class MapboxMap {
* @param source the source to remove
* @return the source
*/
- @UiThread
@Nullable
public Source removeSource(@NonNull Source source) {
return nativeMapView.removeSource(source);
@@ -482,7 +462,6 @@ public final class MapboxMap {
* @param name the name of the image
* @param image the pre-multiplied Bitmap
*/
- @UiThread
public void addImage(@NonNull String name, @NonNull Bitmap image) {
nativeMapView.addImage(name, image);
}
@@ -492,11 +471,14 @@ public final class MapboxMap {
*
* @param name the name of the image to remove
*/
- @UiThread
public void removeImage(String name) {
nativeMapView.removeImage(name);
}
+ public Bitmap getImage(@NonNull String name) {
+ return nativeMapView.getImage(name);
+ }
+
//
// MinZoom
//
@@ -508,7 +490,6 @@ public final class MapboxMap {
*
* @param minZoom The new minimum zoom level.
*/
- @UiThread
public void setMinZoomPreference(
@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) {
transform.setMinZoom(minZoom);
@@ -516,12 +497,11 @@ public final class MapboxMap {
/**
* <p>
- * Gets the maximum zoom level the map can be displayed at.
+ * Gets the minimum zoom level the map can be displayed at.
* </p>
*
* @return The minimum zoom level.
*/
- @UiThread
public double getMinZoomLevel() {
return transform.getMinZoom();
}
@@ -534,10 +514,12 @@ public final class MapboxMap {
* <p>
* Sets the maximum zoom level the map can be displayed at.
* </p>
+ * <p>
+ * The default maximum zoomn level is 22. The upper bound for this value is 25.5.
+ * </p>
*
* @param maxZoom The new maximum zoom level.
*/
- @UiThread
public void setMaxZoomPreference(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM,
to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) {
transform.setMaxZoom(maxZoom);
@@ -550,7 +532,6 @@ public final class MapboxMap {
*
* @return The maximum zoom level.
*/
- @UiThread
public double getMaxZoomLevel() {
return transform.getMaxZoom();
}
@@ -576,7 +557,10 @@ public final class MapboxMap {
* Gets the tracking interface settings for the map.
*
* @return the TrackingSettings asssociated with this map
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
+ @Deprecated
public TrackingSettings getTrackingSettings() {
return trackingSettings;
}
@@ -589,7 +573,10 @@ public final class MapboxMap {
* Gets the settings of the user location for the map.
*
* @return the MyLocationViewSettings associated with this map
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
+ @Deprecated
public MyLocationViewSettings getMyLocationViewSettings() {
return myLocationViewSettings;
}
@@ -627,6 +614,47 @@ public final class MapboxMap {
//
/**
+ * Moves the center of the screen to a latitude and longitude specified by a LatLng object. This centers the
+ * camera on the LatLng object.
+ *
+ * @param latLng Target location to change to
+ */
+ public void setLatLng(@NonNull LatLng latLng) {
+ nativeMapView.setLatLng(latLng);
+ }
+
+ /**
+ * Moves the camera viewpoint to a particular zoom level.
+ *
+ * @param zoom Zoom level to change to
+ */
+ public void setZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double zoom) {
+ if (focalPoint == null) {
+ focalPoint = new PointF(nativeMapView.getWidth() / 2, nativeMapView.getHeight() / 2);
+ }
+ nativeMapView.setZoom(zoom, focalPoint, 0);
+ }
+
+ /**
+ * Moves the camera viewpoint angle to a particular angle in degrees.
+ *
+ * @param tilt Tilt angle to change to
+ */
+ public void setTilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT, to = MapboxConstants.MAXIMUM_TILT) double tilt) {
+ nativeMapView.setPitch(tilt, 0);
+ }
+
+ /**
+ * Moves the camera viewpoint direction to a particular angle in degrees.
+ *
+ * @param bearing Direction angle to change to
+ */
+ public void setBearing(@FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION)
+ double bearing) {
+ nativeMapView.setBearing(bearing);
+ }
+
+ /**
* Cancels ongoing animations.
* <p>
* This invokes the {@link CancelableCallback} for ongoing camera updates.
@@ -665,7 +693,6 @@ public final class MapboxMap {
*
* @param update The change that should be applied to the camera.
*/
- @UiThread
public final void moveCamera(CameraUpdate update) {
moveCamera(update, null);
}
@@ -678,7 +705,6 @@ public final class MapboxMap {
* @param update The change that should be applied to the camera
* @param callback the callback to be invoked when an animation finishes or is canceled
*/
- @UiThread
public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) {
new Handler().post(new Runnable() {
@Override
@@ -699,7 +725,6 @@ public final class MapboxMap {
* @param update The change that should be applied to the camera.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void easeCamera(CameraUpdate update) {
easeCamera(update, MapboxConstants.ANIMATION_DURATION);
}
@@ -714,7 +739,6 @@ public final class MapboxMap {
* positive, otherwise an IllegalArgumentException will be thrown.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void easeCamera(CameraUpdate update, int durationMs) {
easeCamera(update, durationMs, null);
}
@@ -738,7 +762,6 @@ public final class MapboxMap {
* Do not update or ease the camera from within onCancel().
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
easeCamera(update, durationMs, true, callback);
}
@@ -757,7 +780,6 @@ public final class MapboxMap {
* positive, otherwise an IllegalArgumentException will be thrown.
* @param easingInterpolator True for easing interpolator, false for linear.
*/
- @UiThread
public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) {
easeCamera(update, durationMs, easingInterpolator, null);
}
@@ -783,7 +805,6 @@ public final class MapboxMap {
* by a later camera movement or a user gesture, onCancel() will be called.
* Do not update or ease the camera from within onCancel().
*/
- @UiThread
public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator,
final MapboxMap.CancelableCallback callback) {
easeCamera(update, durationMs, easingInterpolator, callback, false);
@@ -811,7 +832,6 @@ public final class MapboxMap {
* Do not update or ease the camera from within onCancel().
* @param isDismissable true will allow animated camera changes dismiss a tracking mode.
*/
- @UiThread
public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator,
final MapboxMap.CancelableCallback callback, final boolean isDismissable) {
new Handler().post(new Runnable() {
@@ -831,7 +851,6 @@ public final class MapboxMap {
* @param update The change that should be applied to the camera.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(CameraUpdate update) {
animateCamera(update, MapboxConstants.ANIMATION_DURATION, null);
}
@@ -848,7 +867,6 @@ public final class MapboxMap {
* called. Do not update or animate the camera from within onCancel().
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) {
animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback);
}
@@ -864,7 +882,6 @@ public final class MapboxMap {
* positive, otherwise an IllegalArgumentException will be thrown.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(CameraUpdate update, int durationMs) {
animateCamera(update, durationMs, null);
}
@@ -887,7 +904,6 @@ public final class MapboxMap {
* isn't required, leave it as null.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(final CameraUpdate update, final int durationMs,
final MapboxMap.CancelableCallback callback) {
new Handler().post(new Runnable() {
@@ -958,7 +974,6 @@ public final class MapboxMap {
*
* @return If true, map debug information is currently shown.
*/
- @UiThread
public boolean isDebugActive() {
return nativeMapView.getDebug();
}
@@ -971,7 +986,6 @@ public final class MapboxMap {
*
* @param debugActive If true, map debug information is shown.
*/
- @UiThread
public void setDebugActive(boolean debugActive) {
nativeMapView.setDebug(debugActive);
}
@@ -985,7 +999,6 @@ public final class MapboxMap {
*
* @see #isDebugActive()
*/
- @UiThread
public void cycleDebugOptions() {
nativeMapView.cycleDebugOptions();
}
@@ -1035,7 +1048,6 @@ public final class MapboxMap {
* @param url The URL of the map style
* @see Style
*/
- @UiThread
public void setStyleUrl(@NonNull String url) {
setStyleUrl(url, null);
}
@@ -1068,7 +1080,6 @@ public final class MapboxMap {
* @param callback The callback that is invoked when the style has loaded.
* @see Style
*/
- @UiThread
public void setStyleUrl(@NonNull final String url, @Nullable final OnStyleLoadedListener callback) {
if (callback != null) {
nativeMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
@@ -1100,7 +1111,6 @@ public final class MapboxMap {
* @param style The bundled style.
* @see Style
*/
- @UiThread
public void setStyle(@Style.StyleUrl String style) {
setStyleUrl(style);
}
@@ -1117,7 +1127,6 @@ public final class MapboxMap {
* @param callback The callback to be invoked when the style has finished loading
* @see Style
*/
- @UiThread
public void setStyle(@Style.StyleUrl String style, @Nullable OnStyleLoadedListener callback) {
setStyleUrl(style, callback);
}
@@ -1135,16 +1144,36 @@ public final class MapboxMap {
}
/**
- * Returns the map style currently displayed in the map view.
+ * Returns the map style url currently displayed in the map view.
*
* @return The URL of the map style
*/
- @UiThread
@Nullable
public String getStyleUrl() {
return nativeMapView.getStyleUrl();
}
+ /**
+ * Loads a new map style from a json string.
+ * <p>
+ * If the style fails to load or an invalid style URL is set, the map view will become blank.
+ * An error message will be logged in the Android logcat and {@link MapView#DID_FAIL_LOADING_MAP} event will be
+ * sent.
+ * </p>
+ */
+ public void setStyleJson(@NonNull String styleJson) {
+ nativeMapView.setStyleJson(styleJson);
+ }
+
+ /**
+ * Returns the map style json currently displayed in the map view.
+ *
+ * @return The json of the map style
+ */
+ public String getStyleJson() {
+ return nativeMapView.getStyleJson();
+ }
+
//
// Annotations
//
@@ -1159,7 +1188,6 @@ public final class MapboxMap {
* @param markerOptions A marker options object that defines how to render the marker
* @return The {@code Marker} that was added to the map
*/
- @UiThread
@NonNull
public Marker addMarker(@NonNull MarkerOptions markerOptions) {
return annotationManager.addMarker(markerOptions, this);
@@ -1175,7 +1203,6 @@ public final class MapboxMap {
* @param markerOptions A marker options object that defines how to render the marker
* @return The {@code Marker} that was added to the map
*/
- @UiThread
@NonNull
public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) {
return annotationManager.addMarker(markerOptions, this);
@@ -1190,9 +1217,11 @@ public final class MapboxMap {
*
* @param markerOptions A marker options object that defines how to render the marker
* @return The {@code Marker} that was added to the map
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
@NonNull
+ @Deprecated
public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) {
return annotationManager.addMarker(markerOptions, this, null);
}
@@ -1207,8 +1236,10 @@ public final class MapboxMap {
* @param markerOptions A marker options object that defines how to render the marker
* @param onMarkerViewAddedListener Callback invoked when the View has been added to the map
* @return The {@code Marker} that was added to the map
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
+ @Deprecated
@NonNull
public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions,
final MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) {
@@ -1224,9 +1255,11 @@ public final class MapboxMap {
*
* @param markerViewOptions A list of markerView options objects that defines how to render the markers
* @return A list of the {@code MarkerView}s that were added to the map
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
@NonNull
+ @Deprecated
public List<MarkerView> addMarkerViews(@NonNull List<? extends
BaseMarkerViewOptions> markerViewOptions) {
return annotationManager.addMarkerViews(markerViewOptions, this);
@@ -1237,9 +1270,11 @@ public final class MapboxMap {
*
* @param rect the rectangular area on the map to query for markerViews
* @return A list of the markerViews that were found in the rectangle
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
@NonNull
+ @Deprecated
public List<MarkerView> getMarkerViewsInRect(@NonNull RectF rect) {
return annotationManager.getMarkerViewsInRect(rect);
}
@@ -1254,7 +1289,6 @@ public final class MapboxMap {
* @param markerOptionsList A list of marker options objects that defines how to render the markers
* @return A list of the {@code Marker}s that were added to the map
*/
- @UiThread
@NonNull
public List<Marker> addMarkers(@NonNull List<? extends
BaseMarkerOptions> markerOptionsList) {
@@ -1268,7 +1302,6 @@ public final class MapboxMap {
*
* @param updatedMarker An updated marker object
*/
- @UiThread
public void updateMarker(@NonNull Marker updatedMarker) {
annotationManager.updateMarker(updatedMarker, this);
}
@@ -1279,7 +1312,6 @@ public final class MapboxMap {
* @param polylineOptions A polyline options object that defines how to render the polyline
* @return The {@code Polyine} that was added to the map
*/
- @UiThread
@NonNull
public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) {
return annotationManager.addPolyline(polylineOptions, this);
@@ -1291,7 +1323,6 @@ public final class MapboxMap {
* @param polylineOptionsList A list of polyline options objects that defines how to render the polylines.
* @return A list of the {@code Polyline}s that were added to the map.
*/
- @UiThread
@NonNull
public List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) {
return annotationManager.addPolylines(polylineOptionsList, this);
@@ -1302,7 +1333,6 @@ public final class MapboxMap {
*
* @param polyline An updated polyline object.
*/
- @UiThread
public void updatePolyline(Polyline polyline) {
annotationManager.updatePolyline(polyline);
}
@@ -1313,7 +1343,6 @@ public final class MapboxMap {
* @param polygonOptions A polygon options object that defines how to render the polygon.
* @return The {@code Polygon} that was added to the map.
*/
- @UiThread
@NonNull
public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) {
return annotationManager.addPolygon(polygonOptions, this);
@@ -1325,7 +1354,6 @@ public final class MapboxMap {
* @param polygonOptionsList A list of polygon options objects that defines how to render the polygons
* @return A list of the {@code Polygon}s that were added to the map
*/
- @UiThread
@NonNull
public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) {
return annotationManager.addPolygons(polygonOptionsList, this);
@@ -1336,7 +1364,6 @@ public final class MapboxMap {
*
* @param polygon An updated polygon object
*/
- @UiThread
public void updatePolygon(Polygon polygon) {
annotationManager.updatePolygon(polygon);
}
@@ -1349,7 +1376,6 @@ public final class MapboxMap {
*
* @param marker Marker to remove
*/
- @UiThread
public void removeMarker(@NonNull Marker marker) {
annotationManager.removeAnnotation(marker);
}
@@ -1362,7 +1388,6 @@ public final class MapboxMap {
*
* @param polyline Polyline to remove
*/
- @UiThread
public void removePolyline(@NonNull Polyline polyline) {
annotationManager.removeAnnotation(polyline);
}
@@ -1375,7 +1400,6 @@ public final class MapboxMap {
*
* @param polygon Polygon to remove
*/
- @UiThread
public void removePolygon(@NonNull Polygon polygon) {
annotationManager.removeAnnotation(polygon);
}
@@ -1385,7 +1409,6 @@ public final class MapboxMap {
*
* @param annotation The annotation object to remove.
*/
- @UiThread
public void removeAnnotation(@NonNull Annotation annotation) {
annotationManager.removeAnnotation(annotation);
}
@@ -1395,7 +1418,6 @@ public final class MapboxMap {
*
* @param id The identifier associated to the annotation to be removed
*/
- @UiThread
public void removeAnnotation(long id) {
annotationManager.removeAnnotation(id);
}
@@ -1405,7 +1427,6 @@ public final class MapboxMap {
*
* @param annotationList A list of annotation objects to remove.
*/
- @UiThread
public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) {
annotationManager.removeAnnotations(annotationList);
}
@@ -1413,7 +1434,6 @@ public final class MapboxMap {
/**
* Removes all annotations from the map.
*/
- @UiThread
public void removeAnnotations() {
annotationManager.removeAnnotations();
}
@@ -1421,7 +1441,6 @@ public final class MapboxMap {
/**
* Removes all markers, polylines, polygons, overlays, etc from the map.
*/
- @UiThread
public void clear() {
annotationManager.removeAnnotations();
}
@@ -1487,7 +1506,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
*/
- @UiThread
public void setOnMarkerClickListener(@Nullable OnMarkerClickListener listener) {
annotationManager.setOnMarkerClickListener(listener);
}
@@ -1498,7 +1516,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on a polygon.
* To unset the callback, use null.
*/
- @UiThread
public void setOnPolygonClickListener(@Nullable OnPolygonClickListener listener) {
annotationManager.setOnPolygonClickListener(listener);
}
@@ -1509,7 +1526,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on a polyline.
* To unset the callback, use null.
*/
- @UiThread
public void setOnPolylineClickListener(@Nullable OnPolylineClickListener listener) {
annotationManager.setOnPolylineClickListener(listener);
}
@@ -1524,7 +1540,6 @@ public final class MapboxMap {
*
* @param marker The marker to select.
*/
- @UiThread
public void selectMarker(@NonNull Marker marker) {
if (marker == null) {
Timber.w("marker was null, so just returning");
@@ -1536,7 +1551,6 @@ public final class MapboxMap {
/**
* Deselects any currently selected marker. All markers will have it's info window closed.
*/
- @UiThread
public void deselectMarkers() {
annotationManager.deselectMarkers();
}
@@ -1546,7 +1560,6 @@ public final class MapboxMap {
*
* @param marker the marker to deselect
*/
- @UiThread
public void deselectMarker(@NonNull Marker marker) {
annotationManager.deselectMarker(marker);
}
@@ -1556,7 +1569,6 @@ public final class MapboxMap {
*
* @return The currently selected marker.
*/
- @UiThread
public List<Marker> getSelectedMarkers() {
return annotationManager.getSelectedMarkers();
}
@@ -1584,7 +1596,6 @@ public final class MapboxMap {
* @param infoWindowAdapter The callback to be invoked when an info window will be shown.
* To unset the callback, use null.
*/
- @UiThread
public void setInfoWindowAdapter(@Nullable InfoWindowAdapter infoWindowAdapter) {
annotationManager.getInfoWindowManager().setInfoWindowAdapter(infoWindowAdapter);
}
@@ -1594,7 +1605,6 @@ public final class MapboxMap {
*
* @return The callback to be invoked when an info window will be shown.
*/
- @UiThread
@Nullable
public InfoWindowAdapter getInfoWindowAdapter() {
return annotationManager.getInfoWindowManager().getInfoWindowAdapter();
@@ -1605,7 +1615,6 @@ public final class MapboxMap {
*
* @param allow If true, map allows concurrent multiple infowindows to be shown.
*/
- @UiThread
public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) {
annotationManager.getInfoWindowManager().setAllowConcurrentMultipleOpenInfoWindows(allow);
}
@@ -1615,7 +1624,6 @@ public final class MapboxMap {
*
* @return If true, map allows concurrent multiple infowindows to be shown.
*/
- @UiThread
public boolean isAllowConcurrentMultipleOpenInfoWindows() {
return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows();
}
@@ -1659,6 +1667,30 @@ public final class MapboxMap {
return cameraPosition;
}
+ /**
+ * Get a camera position that fits a provided shape with a given bearing and padding.
+ *
+ * @param geometry the geometry to constrain the map with
+ * @param bearing the bearing at which to compute the geometry's bounds
+ * @param padding the padding to apply to the bounds
+ * @return the camera position that fits the bounds and padding
+ */
+ public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) {
+ // calculate and set additional bounds padding
+ int[] mapPadding = getPadding();
+ for (int i = 0; i < padding.length; i++) {
+ padding[i] = mapPadding[i] + padding[i];
+ }
+ projection.setContentPadding(padding, myLocationViewSettings.getPadding());
+
+ // get padded camera position from LatLngBounds
+ CameraPosition cameraPosition = nativeMapView.getCameraForGeometry(geometry, bearing);
+
+ // reset map padding
+ setPadding(mapPadding);
+ return cameraPosition;
+ }
+
//
// Padding
//
@@ -1709,7 +1741,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked on every camera change position.
* To unset the callback, use null.
*/
- @UiThread
@Deprecated
public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) {
transform.setOnCameraChangeListener(listener);
@@ -1720,7 +1751,6 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
cameraChangeDispatcher.setOnCameraIdleListener(listener);
}
@@ -1730,7 +1760,6 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener);
}
@@ -1740,8 +1769,7 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
- public void setOnCameraMoveStartedistener(@Nullable OnCameraMoveStartedListener listener) {
+ public void setOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.setOnCameraMoveStartedListener(listener);
}
@@ -1750,7 +1778,6 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- @UiThread
public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
cameraChangeDispatcher.setOnCameraMoveListener(listener);
}
@@ -1761,9 +1788,9 @@ public final class MapboxMap {
* @param listener The callback that's invoked on every frame rendered to the map view.
* To unset the callback, use null.
*/
- @UiThread
public void setOnFpsChangedListener(@Nullable OnFpsChangedListener listener) {
onFpsChangedListener = listener;
+ nativeMapView.setOnFpsChangedListener(listener);
}
// used by MapView
@@ -1777,7 +1804,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the map is scrolled.
* To unset the callback, use null.
*/
- @UiThread
public void setOnScrollListener(@Nullable OnScrollListener listener) {
onRegisterTouchListener.onRegisterScrollListener(listener);
}
@@ -1788,7 +1814,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the map is flinged.
* To unset the callback, use null.
*/
- @UiThread
public void setOnFlingListener(@Nullable OnFlingListener listener) {
onRegisterTouchListener.onRegisterFlingListener(listener);
}
@@ -1799,7 +1824,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on the map view.
* To unset the callback, use null.
*/
- @UiThread
public void setOnMapClickListener(@Nullable OnMapClickListener listener) {
onRegisterTouchListener.onRegisterMapClickListener(listener);
}
@@ -1810,7 +1834,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user long clicks on the map view.
* To unset the callback, use null.
*/
- @UiThread
public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
onRegisterTouchListener.onRegisterMapLongClickListener(listener);
}
@@ -1821,7 +1844,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on an info window.
* To unset the callback, use null.
*/
- @UiThread
public void setOnInfoWindowClickListener(@Nullable OnInfoWindowClickListener listener) {
annotationManager.getInfoWindowManager().setOnInfoWindowClickListener(listener);
}
@@ -1831,7 +1853,6 @@ public final class MapboxMap {
*
* @return Current active InfoWindow Click Listener
*/
- @UiThread
public OnInfoWindowClickListener getOnInfoWindowClickListener() {
return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener();
}
@@ -1842,7 +1863,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when a marker's info window is long pressed. To unset the callback,
* use null.
*/
- @UiThread
public void setOnInfoWindowLongClickListener(@Nullable OnInfoWindowLongClickListener
listener) {
annotationManager.getInfoWindowManager().setOnInfoWindowLongClickListener(listener);
@@ -1871,7 +1891,6 @@ public final class MapboxMap {
*
* @return Current active InfoWindow Close Listener
*/
- @UiThread
public OnInfoWindowCloseListener getOnInfoWindowCloseListener() {
return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener();
}
@@ -1884,8 +1903,10 @@ public final class MapboxMap {
* Returns the status of the my-location layer.
*
* @return True if the my-location layer is enabled, false otherwise.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public boolean isMyLocationEnabled() {
return trackingSettings.isMyLocationEnabled();
}
@@ -1900,8 +1921,10 @@ public final class MapboxMap {
* android.Manifest.permission#ACCESS_COARSE_LOCATION or android.Manifest.permission#ACCESS_FINE_LOCATION.
*
* @param enabled True to enable; false to disable.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setMyLocationEnabled(boolean enabled) {
trackingSettings.setMyLocationEnabled(enabled);
}
@@ -1910,9 +1933,11 @@ public final class MapboxMap {
* Returns the currently displayed user location, or null if there is no location data available.
*
* @return The currently displayed user location.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
@Nullable
+ @Deprecated
public Location getMyLocation() {
return trackingSettings.getMyLocation();
}
@@ -1923,8 +1948,10 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener
listener) {
trackingSettings.setOnMyLocationChangeListener(listener);
@@ -1934,8 +1961,10 @@ public final class MapboxMap {
* Replaces the location source of the my-location layer.
*
* @param locationSource A {@link LocationEngine} location source to use in the my-location layer.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setLocationSource(@Nullable LocationEngine locationSource) {
trackingSettings.setLocationSource(locationSource);
}
@@ -1945,8 +1974,10 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the location tracking mode changes.
* To unset the callback, use null.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyLocationTrackingModeChangeListener(
@Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) {
trackingSettings.setOnMyLocationTrackingModeChangeListener(listener);
@@ -1957,8 +1988,10 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the bearing tracking mode changes.
* To unset the callback, use null.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) {
trackingSettings.setOnMyBearingTrackingModeChangeListener(listener);
}
@@ -1972,7 +2005,6 @@ public final class MapboxMap {
*
* @param callback Callback method invoked when the snapshot is taken.
*/
- @UiThread
public void snapshot(@NonNull SnapshotReadyCallback callback) {
nativeMapView.addSnapshotCallback(callback);
}
@@ -1984,7 +2016,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String...
layerIds) {
@@ -1999,7 +2030,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates,
@Nullable Filter.Statement filter,
@@ -2014,7 +2044,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
@Nullable String... layerIds) {
@@ -2029,7 +2058,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
@Nullable Filter.Statement filter,
@@ -2037,6 +2065,15 @@ public final class MapboxMap {
return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter);
}
+ FocalPointChangeListener createFocalPointChangeListener() {
+ return new FocalPointChangeListener() {
+ @Override
+ public void onFocalPointChanged(PointF pointF) {
+ focalPoint = pointF;
+ }
+ };
+ }
+
//
// Interfaces
//
@@ -2135,6 +2172,21 @@ public final class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked for when the compass is animating.
+ */
+ public interface OnCompassAnimationListener {
+ /**
+ * Called repeatedly as the compass continues to move after clicking on it.
+ */
+ void onCompassAnimation();
+
+ /**
+ * Called when compass animation has ended.
+ */
+ void onCompassAnimationFinished();
+ }
+
+ /**
* Interface definition for a callback to be invoked when a frame is rendered to the map view.
*
* @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener)
@@ -2299,7 +2351,10 @@ public final class MapboxMap {
* Interface definition for a callback to be invoked when an MarkerView will be shown.
*
* @param <U> the instance type of MarkerView
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+ @Deprecated
public abstract static class MarkerViewAdapter<U extends MarkerView> {
private Context context;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
index 94e2fbcf3b..7b979f5563 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
@@ -85,9 +85,6 @@ public class MapboxMapOptions implements Parcelable {
private String apiBaseUrl;
- @Deprecated
- private boolean textureMode;
-
private String style;
/**
@@ -155,7 +152,7 @@ public class MapboxMapOptions implements Parcelable {
style = in.readString();
apiBaseUrl = in.readString();
- textureMode = in.readByte() != 0;
+
prefetchesTiles = in.readByte() != 0;
zMediaOverlay = in.readByte() != 0;
}
@@ -299,8 +296,6 @@ public class MapboxMapOptions implements Parcelable {
ColorUtils.getPrimaryColor(context)));
mapboxMapOptions.myLocationAccuracyThreshold(
typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0));
- mapboxMapOptions.textureMode(
- typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false));
mapboxMapOptions.setPrefetchesTiles(
typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true));
mapboxMapOptions.renderSurfaceOnTop(
@@ -704,22 +699,6 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Enable TextureView as rendered surface.
- * <p>
- * Since the 4.2.0 release we replaced our TextureView with an SurfaceView implemenation.
- * Enabling this option will use the deprecated TextureView instead.
- * </p>
- *
- * @param textureMode True to enable texture mode
- * @return This
- * @deprecated As of the 4.2.0 release, using TextureView is deprecated.
- */
- public MapboxMapOptions textureMode(boolean textureMode) {
- this.textureMode = textureMode;
- return this;
- }
-
- /**
* Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render
* a low resolution preview while more detailed tiles are loaded.
* Enabled by default
@@ -1070,16 +1049,6 @@ public class MapboxMapOptions implements Parcelable {
return debugActive;
}
- /**
- * Returns true if TextureView is being used a render view.
- *
- * @return True if TextureView is used.
- * @deprecated As of the 4.2.0 release, using TextureView is deprecated.
- */
- public boolean getTextureMode() {
- return textureMode;
- }
-
public static final Parcelable.Creator<MapboxMapOptions> CREATOR = new Parcelable.Creator<MapboxMapOptions>() {
public MapboxMapOptions createFromParcel(Parcel in) {
return new MapboxMapOptions(in);
@@ -1143,7 +1112,7 @@ public class MapboxMapOptions implements Parcelable {
dest.writeString(style);
dest.writeString(apiBaseUrl);
- dest.writeByte((byte) (textureMode ? 1 : 0));
+
dest.writeByte((byte) (prefetchesTiles ? 1 : 0));
dest.writeByte((byte) (zMediaOverlay ? 1 : 0));
}
@@ -1320,7 +1289,6 @@ public class MapboxMapOptions implements Parcelable {
result = 31 * result + (myLocationAccuracyThreshold != +0.0f
? Float.floatToIntBits(myLocationAccuracyThreshold) : 0);
result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0);
- result = 31 * result + (textureMode ? 1 : 0);
result = 31 * result + (style != null ? style.hashCode() : 0);
result = 31 * result + (prefetchesTiles ? 1 : 0);
result = 31 * result + (zMediaOverlay ? 1 : 0);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
index b6bee199a3..072382ce07 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
@@ -53,7 +53,7 @@ class MarkerContainer implements Markers {
mapboxMap) {
int count = markerOptionsList.size();
List<Marker> markers = new ArrayList<>(count);
- if (count > 0) {
+ if (nativeMapView != null && count > 0) {
BaseMarkerOptions markerOptions;
Marker marker;
for (int i = 0; i < count; i++) {
@@ -63,26 +63,13 @@ class MarkerContainer implements Markers {
}
if (markers.size() > 0) {
- long[] ids = null;
- if (nativeMapView != null) {
- ids = nativeMapView.addMarkers(markers);
+ long[] ids = nativeMapView.addMarkers(markers);
+ for (int i = 0; i < ids.length; i++) {
+ Marker createdMarker = markers.get(i);
+ createdMarker.setMapboxMap(mapboxMap);
+ createdMarker.setId(ids[i]);
+ annotations.put(ids[i], createdMarker);
}
-
- long id = 0;
- Marker m;
- for (int i = 0; i < markers.size(); i++) {
- m = markers.get(i);
- m.setMapboxMap(mapboxMap);
- if (ids != null) {
- id = ids[i];
- } else {
- // unit test
- id++;
- }
- m.setId(id);
- annotations.put(id, m);
- }
-
}
}
return markers;
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 52596358e2..bd8a54783e 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
@@ -9,7 +9,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
-import android.view.Surface;
import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.annotations.Icon;
@@ -17,10 +16,10 @@ import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
+import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
import com.mapbox.mapboxsdk.storage.FileSource;
import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException;
import com.mapbox.mapboxsdk.style.layers.Filter;
@@ -30,6 +29,7 @@ import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.Geometry;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -53,6 +53,9 @@ final class NativeMapView {
//Hold a reference to prevent it from being GC'd as long as it's used on the native side
private final FileSource fileSource;
+ // Used to schedule work on the MapRenderer Thread
+ private MapRenderer mapRenderer;
+
// Device density
private final float pixelRatio;
@@ -67,15 +70,15 @@ final class NativeMapView {
// Constructors
//
- public NativeMapView(MapView mapView) {
+ public NativeMapView(final MapView mapView, MapRenderer mapRenderer) {
+ this.mapRenderer = mapRenderer;
+ this.mapView = mapView;
+
Context context = mapView.getContext();
fileSource = FileSource.getInstance(context);
-
pixelRatio = context.getResources().getDisplayMetrics().density;
- this.mapView = mapView;
- String programCacheDir = context.getCacheDir().getAbsolutePath();
- nativeInitialize(this, fileSource, pixelRatio, programCacheDir);
+ nativeInitialize(this, fileSource, mapRenderer, pixelRatio);
}
//
@@ -84,9 +87,10 @@ final class NativeMapView {
private boolean isDestroyedOn(String callingMethod) {
if (destroyed && !TextUtils.isEmpty(callingMethod)) {
- Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE,
+ Timber.e(
"You're calling `%s` after the `MapView` was destroyed, were you invoking it after `onDestroy()`?",
- callingMethod));
+ callingMethod
+ );
}
return destroyed;
}
@@ -97,32 +101,12 @@ final class NativeMapView {
destroyed = true;
}
- public void createSurface(Surface surface) {
- if (isDestroyedOn("createSurface")) {
- return;
- }
- nativeCreateSurface(surface);
- }
-
- public void destroySurface() {
- if (isDestroyedOn("destroySurface")) {
- return;
- }
- nativeDestroySurface();
- }
-
public void update() {
if (isDestroyedOn("update")) {
return;
}
- nativeUpdate();
- }
- public void render() {
- if (isDestroyedOn("render")) {
- return;
- }
- nativeRender();
+ mapRenderer.requestRender();
}
public void resizeView(int width, int height) {
@@ -143,41 +127,18 @@ final class NativeMapView {
if (width > 65535) {
// we have seen edge cases where devices return incorrect values #6111
Timber.e("Device returned an out of range width size, "
- + "capping value at 65535 instead of " + width);
+ + "capping value at 65535 instead of %s", width);
width = 65535;
}
if (height > 65535) {
// we have seen edge cases where devices return incorrect values #6111
Timber.e("Device returned an out of range height size, "
- + "capping value at 65535 instead of " + height);
+ + "capping value at 65535 instead of %s", height);
height = 65535;
}
- nativeResizeView(width, height);
- }
- public void resizeFramebuffer(int fbWidth, int fbHeight) {
- if (isDestroyedOn("resizeFramebuffer")) {
- return;
- }
- if (fbWidth < 0) {
- throw new IllegalArgumentException("fbWidth cannot be negative.");
- }
-
- if (fbHeight < 0) {
- throw new IllegalArgumentException("fbHeight cannot be negative.");
- }
-
- if (fbWidth > 65535) {
- throw new IllegalArgumentException(
- "fbWidth cannot be greater than 65535.");
- }
-
- if (fbHeight > 65535) {
- throw new IllegalArgumentException(
- "fbHeight cannot be greater than 65535.");
- }
- nativeResizeFramebuffer(fbWidth, fbHeight);
+ nativeResizeView(width, height);
}
public void setStyleUrl(String url) {
@@ -272,6 +233,13 @@ final class NativeMapView {
return nativeGetCameraForLatLngBounds(latLngBounds);
}
+ public CameraPosition getCameraForGeometry(Geometry geometry, double bearing) {
+ if (isDestroyedOn("getCameraForGeometry")) {
+ return null;
+ }
+ return nativeGetCameraForGeometry(geometry, bearing);
+ }
+
public void resetPosition() {
if (isDestroyedOn("resetPosition")) {
return;
@@ -507,6 +475,13 @@ final class NativeMapView {
nativeAddAnnotationIcon(symbol, width, height, scale, pixels);
}
+ public void removeAnnotationIcon(String symbol) {
+ if (isDestroyedOn("removeAnnotationIcon")) {
+ return;
+ }
+ nativeRemoveAnnotationIcon(symbol);
+ }
+
public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) {
if (isDestroyedOn("setVisibleCoordinateBounds")) {
return;
@@ -542,13 +517,6 @@ final class NativeMapView {
return nativeGetDebug();
}
- public void setEnableFps(boolean enable) {
- if (isDestroyedOn("setEnableFps")) {
- return;
- }
- nativeSetEnableFps(enable);
- }
-
public boolean isFullyLoaded() {
if (isDestroyedOn("isFullyLoaded")) {
return false;
@@ -801,6 +769,13 @@ final class NativeMapView {
nativeRemoveImage(name);
}
+ public Bitmap getImage(String name) {
+ if (isDestroyedOn("getImage")) {
+ return null;
+ }
+ return nativeGetImage(name);
+ }
+
// Feature querying
@NonNull
@@ -832,13 +807,6 @@ final class NativeMapView {
return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
}
- public void scheduleTakeSnapshot() {
- if (isDestroyedOn("scheduleTakeSnapshot")) {
- return;
- }
- nativeTakeSnapshot();
- }
-
public void setApiBaseUrl(String baseUrl) {
if (isDestroyedOn("setApiBaseUrl")) {
return;
@@ -861,25 +829,12 @@ final class NativeMapView {
// Callbacks
//
- protected void onInvalidate() {
- if (mapView != null) {
- mapView.onInvalidate();
- }
- }
-
protected void onMapChanged(int rawChange) {
if (mapView != null) {
mapView.onMapChange(rawChange);
}
}
- protected void onFpsChanged(double fps) {
- if (isDestroyedOn("OnFpsChanged")) {
- return;
- }
- mapView.onFpsChanged(fps);
- }
-
protected void onSnapshotReady(Bitmap mapContent) {
if (isDestroyedOn("OnSnapshotReady")) {
return;
@@ -897,31 +852,13 @@ final class NativeMapView {
private native void nativeInitialize(NativeMapView nativeMapView,
FileSource fileSource,
- float pixelRatio,
- String programCacheDir);
+ MapRenderer mapRenderer,
+ float pixelRatio);
private native void nativeDestroy();
- private native void nativeInitializeDisplay();
-
- private native void nativeTerminateDisplay();
-
- private native void nativeInitializeContext();
-
- private native void nativeTerminateContext();
-
- private native void nativeCreateSurface(Object surface);
-
- private native void nativeDestroySurface();
-
- private native void nativeUpdate();
-
- private native void nativeRender();
-
private native void nativeResizeView(int width, int height);
- private native void nativeResizeFramebuffer(int fbWidth, int fbHeight);
-
private native void nativeSetStyleUrl(String url);
private native String nativeGetStyleUrl();
@@ -944,6 +881,8 @@ final class NativeMapView {
private native CameraPosition nativeGetCameraForLatLngBounds(LatLngBounds latLngBounds);
+ private native CameraPosition nativeGetCameraForGeometry(Geometry geometry, double bearing);
+
private native void nativeResetPosition();
private native double nativeGetPitch();
@@ -990,6 +929,8 @@ final class NativeMapView {
private native void nativeAddAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels);
+ private native void nativeRemoveAnnotationIcon(String symbol);
+
private native void nativeSetVisibleCoordinateBounds(LatLng[] coordinates, RectF padding,
double direction, long duration);
@@ -1001,8 +942,6 @@ final class NativeMapView {
private native boolean nativeGetDebug();
- private native void nativeSetEnableFps(boolean enable);
-
private native boolean nativeIsFullyLoaded();
private native void nativeSetReachability(boolean status);
@@ -1069,6 +1008,8 @@ final class NativeMapView {
private native void nativeRemoveImage(String name);
+ private native Bitmap nativeGetImage(String name);
+
private native void nativeUpdatePolygon(long polygonId, Polygon polygon);
private native void nativeUpdatePolyline(long polylineId, Polyline polyline);
@@ -1121,8 +1062,35 @@ final class NativeMapView {
//
void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) {
+ if (isDestroyedOn("addSnapshotCallback")) {
+ return;
+ }
snapshotReadyCallback = callback;
- scheduleTakeSnapshot();
- render();
+ nativeTakeSnapshot();
+ }
+
+ public void setOnFpsChangedListener(final MapboxMap.OnFpsChangedListener listener) {
+ mapRenderer.queueEvent(new Runnable() {
+
+ @Override
+ public void run() {
+ mapRenderer.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() {
+
+ @Override
+ public void onFpsChanged(final double fps) {
+ mapView.post(new Runnable() {
+
+ @Override
+ public void run() {
+ listener.onFpsChanged(fps);
+ }
+
+ });
+ }
+
+ });
+ }
+
+ });
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
index e626fc3dc0..016862bddc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
@@ -42,7 +42,7 @@ class PolygonContainer implements Polygons {
Polygon polygon;
List<Polygon> polygons = new ArrayList<>(count);
- if (count > 0) {
+ if (nativeMapView != null && count > 0) {
for (PolygonOptions polygonOptions : polygonOptionsList) {
polygon = polygonOptions.getPolygon();
if (!polygon.getPoints().isEmpty()) {
@@ -50,23 +50,12 @@ class PolygonContainer implements Polygons {
}
}
- long[] ids = null;
- if (nativeMapView != null) {
- ids = nativeMapView.addPolygons(polygons);
- }
-
- long id = 0;
- for (int i = 0; i < polygons.size(); i++) {
+ long[] ids = nativeMapView.addPolygons(polygons);
+ for (int i = 0; i < ids.length; i++) {
polygon = polygons.get(i);
polygon.setMapboxMap(mapboxMap);
- if (ids != null) {
- id = ids[i];
- } else {
- // unit test
- id++;
- }
- polygon.setId(id);
- annotations.put(id, polygon);
+ polygon.setId(ids[i]);
+ annotations.put(ids[i], polygon);
}
}
return polygons;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
index b9a358c0f9..303b25fb55 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
@@ -41,8 +41,7 @@ class PolylineContainer implements Polylines {
int count = polylineOptionsList.size();
Polyline polyline;
List<Polyline> polylines = new ArrayList<>(count);
-
- if (count > 0) {
+ if (nativeMapView != null && count > 0) {
for (PolylineOptions options : polylineOptionsList) {
polyline = options.getPolyline();
if (!polyline.getPoints().isEmpty()) {
@@ -50,25 +49,12 @@ class PolylineContainer implements Polylines {
}
}
- long[] ids = null;
- if (nativeMapView != null) {
- ids = nativeMapView.addPolylines(polylines);
- }
-
- long id = 0;
- Polyline p;
-
- for (int i = 0; i < polylines.size(); i++) {
- p = polylines.get(i);
- p.setMapboxMap(mapboxMap);
- if (ids != null) {
- id = ids[i];
- } else {
- // unit test
- id++;
- }
- p.setId(id);
- annotations.put(id, p);
+ long[] ids = nativeMapView.addPolylines(polylines);
+ for (int i = 0; i < ids.length; i++) {
+ Polyline polylineCreated = polylines.get(i);
+ polylineCreated.setMapboxMap(mapboxMap);
+ polylineCreated.setId(ids[i]);
+ annotations.put(ids[i], polylineCreated);
}
}
return polylines;
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 e0a634521b..16c73b1ca5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
@@ -6,6 +6,7 @@ import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.geometry.VisibleRegion;
/**
@@ -45,6 +46,20 @@ public class Projection {
}
/**
+ * Returns the spherical Mercator projected meters for a LatLng.
+ */
+ public ProjectedMeters getProjectedMetersForLatLng(LatLng latLng) {
+ return nativeMapView.projectedMetersForLatLng(latLng);
+ }
+
+ /**
+ * Returns the LatLng for a spherical Mercator projected meters.
+ */
+ public LatLng getLatLngForProjectedMeters(ProjectedMeters projectedMeters) {
+ return nativeMapView.latLngForProjectedMeters(projectedMeters);
+ }
+
+ /**
* <p>
* Returns the distance spanned by one pixel at the specified latitude and current zoom level.
* </p>
@@ -78,8 +93,6 @@ public class Projection {
* @return The projection of the viewing frustum in its current state.
*/
public VisibleRegion getVisibleRegion() {
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
-
float left = 0;
float right = nativeMapView.getWidth();
float top = 0;
@@ -90,12 +103,13 @@ public class Projection {
LatLng bottomRight = fromScreenLocation(new PointF(right, bottom));
LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom));
- builder.include(topLeft)
- .include(topRight)
- .include(bottomRight)
- .include(bottomLeft);
-
- return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, builder.build());
+ return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight,
+ LatLngBounds.from(
+ topRight.getLatitude(),
+ topRight.getLongitude(),
+ bottomLeft.getLatitude(),
+ bottomLeft.getLongitude())
+ );
}
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
index bd0bf7c83b..6881ca067b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
@@ -20,7 +20,11 @@ import timber.log.Timber;
/**
* Settings for the user location and bearing tracking of a MapboxMap.
+ *
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
+@Deprecated
public final class TrackingSettings {
private final MyLocationView myLocationView;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
index d788b7772b..6f63c2eba8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
@@ -346,7 +346,7 @@ final class Transform implements MapView.OnMapChangedListener {
void setMinZoom(double minZoom) {
if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) {
- Timber.e("Not setting minZoomPreference, value is in unsupported range: " + minZoom);
+ Timber.e("Not setting minZoomPreference, value is in unsupported range: %s", minZoom);
return;
}
mapView.setMinZoom(minZoom);
@@ -358,7 +358,7 @@ final class Transform implements MapView.OnMapChangedListener {
void setMaxZoom(double maxZoom) {
if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) {
- Timber.e("Not setting maxZoomPreference, value is in unsupported range: " + maxZoom);
+ Timber.e("Not setting maxZoomPreference, value is in unsupported range: %s", maxZoom);
return;
}
mapView.setMaxZoom(maxZoom);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
new file mode 100644
index 0000000000..3f43522e01
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
@@ -0,0 +1,145 @@
+package com.mapbox.mapboxsdk.maps.renderer;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.storage.FileSource;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * The {@link MapRenderer} encapsulates the GL thread.
+ * <p>
+ * Performs actions on the GL thread to manage the GL resources and
+ * render on the one end and acts as a scheduler to request work to
+ * be performed on the GL thread on the other.
+ */
+public class MapRenderer implements GLSurfaceView.Renderer, MapRendererScheduler {
+
+ // Holds the pointer to the native peer after initialisation
+ private long nativePtr = 0;
+
+ private final GLSurfaceView glSurfaceView;
+
+ private MapboxMap.OnFpsChangedListener onFpsChangedListener;
+
+ public MapRenderer(Context context, GLSurfaceView glSurfaceView) {
+ this.glSurfaceView = glSurfaceView;
+
+ FileSource fileSource = FileSource.getInstance(context);
+ float pixelRatio = context.getResources().getDisplayMetrics().density;
+ String programCacheDir = context.getCacheDir().getAbsolutePath();
+
+ // Initialise native peer
+ nativeInitialize(this, fileSource, pixelRatio, programCacheDir);
+ }
+
+ public void setOnFpsChangedListener(MapboxMap.OnFpsChangedListener listener) {
+ onFpsChangedListener = listener;
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ nativeOnSurfaceCreated();
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ if (width < 0) {
+ throw new IllegalArgumentException("fbWidth cannot be negative.");
+ }
+
+ if (height < 0) {
+ throw new IllegalArgumentException("fbHeight cannot be negative.");
+ }
+
+ if (width > 65535) {
+ throw new IllegalArgumentException(
+ "fbWidth cannot be greater than 65535.");
+ }
+
+ if (height > 65535) {
+ throw new IllegalArgumentException(
+ "fbHeight cannot be greater than 65535.");
+ }
+
+ gl.glViewport(0, 0, width, height);
+ nativeOnSurfaceChanged(width, height);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ nativeRender();
+
+ if (onFpsChangedListener != null) {
+ updateFps();
+ }
+ }
+
+ /**
+ * May be called from any thread.
+ * <p>
+ * Called from the renderer frontend to schedule a render.
+ */
+ @Override
+ public void requestRender() {
+ glSurfaceView.requestRender();
+ }
+
+ /**
+ * May be called from any thread.
+ * <p>
+ * Schedules work to be performed on the MapRenderer thread.
+ *
+ * @param runnable the runnable to execute
+ */
+ @Override
+ public void queueEvent(Runnable runnable) {
+ glSurfaceView.queueEvent(runnable);
+ }
+
+ /**
+ * May be called from any thread.
+ * <p>
+ * Called from the native peer to schedule work on the GL
+ * thread. Explicit override for easier to read jni code.
+ *
+ * @param runnable the runnable to execute
+ * @see MapRendererRunnable
+ */
+ void queueEvent(MapRendererRunnable runnable) {
+ this.queueEvent((Runnable) runnable);
+ }
+
+ private native void nativeInitialize(MapRenderer self,
+ FileSource fileSource,
+ float pixelRatio,
+ String programCacheDir);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ private native void nativeOnSurfaceCreated();
+
+ private native void nativeOnSurfaceChanged(int width, int height);
+
+ private native void nativeRender();
+
+ private long frames;
+ private long timeElapsed;
+
+ private void updateFps() {
+ frames++;
+ long currentTime = System.nanoTime();
+ double fps = 0;
+ if (currentTime - timeElapsed >= 1) {
+ fps = frames / ((currentTime - timeElapsed) / 1E9);
+ onFpsChangedListener.onFpsChanged(fps);
+ timeElapsed = currentTime;
+ frames = 0;
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java
new file mode 100644
index 0000000000..28246fe578
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java
@@ -0,0 +1,29 @@
+package com.mapbox.mapboxsdk.maps.renderer;
+
+/**
+ * Peer class for {@link Runnable}s to be scheduled on the {@link MapRenderer} thread.
+ * The actual work is performed in the native peer.
+ */
+class MapRendererRunnable implements Runnable {
+
+ // Holds the pointer to the native peer after initialisation
+ private final long nativePtr;
+
+ /**
+ * Constructed from the native peer constructor
+ *
+ * @param nativePtr the native peer's memory address
+ */
+ MapRendererRunnable(long nativePtr) {
+ this.nativePtr = nativePtr;
+ }
+
+ @Override
+ public native void run();
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ private native void nativeInitialize();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java
new file mode 100644
index 0000000000..7ad4f124d8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java
@@ -0,0 +1,13 @@
+package com.mapbox.mapboxsdk.maps.renderer;
+
+/**
+ * Can be used to schedule work on the map renderer
+ * thread or request a render.
+ */
+public interface MapRendererScheduler {
+
+ void requestRender();
+
+ void queueEvent(Runnable runnable);
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
index 2b327409ae..45f72af1c5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
@@ -1,10 +1,8 @@
package com.mapbox.mapboxsdk.maps.widgets;
import android.content.Context;
-import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
@@ -13,11 +11,8 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
-import com.mapbox.mapboxsdk.maps.FocalPointChangeListener;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import java.lang.ref.WeakReference;
-
/**
* UI element overlaid on a map to show the map's bearing when it isn't true north (0.0). Tapping
* the compass resets the bearing to true north and hides the compass.
@@ -27,16 +22,17 @@ import java.lang.ref.WeakReference;
* use {@link com.mapbox.mapboxsdk.maps.UiSettings}.
* </p>
*/
-public final class CompassView extends AppCompatImageView implements Runnable, FocalPointChangeListener {
+public final class CompassView extends AppCompatImageView implements Runnable {
- private static final long TIME_WAIT_IDLE = 500;
+ public static final long TIME_WAIT_IDLE = 500;
+ public static final long TIME_MAP_NORTH_ANIMATION = 150;
private static final long TIME_FADE_ANIMATION = TIME_WAIT_IDLE;
- private static final long TIME_MAP_NORTH_ANIMATION = 150;
private float rotation = 0.0f;
private boolean fadeCompassViewFacingNorth = true;
private ViewPropertyAnimatorCompat fadeAnimator;
- private PointF focalPoint;
+ private MapboxMap.OnCompassAnimationListener compassAnimationListener;
+ private boolean isAnimating = false;
public CompassView(Context context) {
super(context);
@@ -62,9 +58,12 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
setLayoutParams(lp);
}
- // TODO refactor MapboxMap and replace with interface
- public void setMapboxMap(@NonNull MapboxMap mapboxMap) {
- setOnClickListener(new CompassClickListener(mapboxMap, this));
+ public void injectCompassAnimationListener(@NonNull MapboxMap.OnCompassAnimationListener compassAnimationListener) {
+ this.compassAnimationListener = compassAnimationListener;
+ }
+
+ public void isAnimating(boolean isAnimating) {
+ this.isAnimating = isAnimating;
}
private void resetAnimation() {
@@ -97,11 +96,6 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
}
}
- @Nullable
- PointF getFocalPoint() {
- return focalPoint;
- }
-
/**
* Updates the direction of the compass.
*
@@ -126,6 +120,7 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
setVisibility(View.VISIBLE);
}
+ notifyCompassAnimationListenerWhenAnimating();
setRotation(rotation);
}
@@ -157,7 +152,8 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
@Override
public void run() {
- if (isFacingNorth() && fadeCompassViewFacingNorth) {
+ if (isHidden()) {
+ compassAnimationListener.onCompassAnimationFinished();
resetAnimation();
setLayerType(View.LAYER_TYPE_HARDWARE, null);
fadeAnimator = ViewCompat.animate(CompassView.this).alpha(0.0f).setDuration(TIME_FADE_ANIMATION);
@@ -172,34 +168,9 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
}
}
- @Override
- public void onFocalPointChanged(PointF pointF) {
- focalPoint = pointF;
- }
-
- static class CompassClickListener implements View.OnClickListener {
-
- private WeakReference<MapboxMap> mapboxMap;
- private WeakReference<CompassView> compassView;
-
- CompassClickListener(final MapboxMap mapboxMap, CompassView compassView) {
- this.mapboxMap = new WeakReference<>(mapboxMap);
- this.compassView = new WeakReference<>(compassView);
- }
-
- @Override
- public void onClick(View view) {
- final MapboxMap mapboxMap = this.mapboxMap.get();
- final CompassView compassView = this.compassView.get();
- if (mapboxMap != null && compassView != null) {
- PointF focalPoint = compassView.getFocalPoint();
- if (focalPoint != null) {
- mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION);
- } else {
- mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION);
- }
- compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION);
- }
+ private void notifyCompassAnimationListenerWhenAnimating() {
+ if (isAnimating) {
+ compassAnimationListener.onCompassAnimation();
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
index 017fdb353a..983ba2550f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
@@ -1,6 +1,7 @@
package com.mapbox.mapboxsdk.maps.widgets;
import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
@@ -48,7 +49,10 @@ import timber.log.Timber;
* <p>
* Use {@link MyLocationViewSettings} to manipulate the state of this view.
* </p>
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
+@Deprecated
public class MyLocationView extends View {
private static final int UNDEFINED_TINT_COLOR = -1;
@@ -76,7 +80,7 @@ public class MyLocationView extends View {
private ValueAnimator locationChangeAnimator;
private ValueAnimator accuracyAnimator;
private ValueAnimator directionAnimator;
- private boolean locationChangeAnimationEnabled;
+ private boolean locationChangeAnimationEnabled = true;
private ValueAnimator.AnimatorUpdateListener invalidateSelfOnUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -775,6 +779,7 @@ public class MyLocationView extends View {
locationSource = new WeakReference<>(locationEngine);
}
+ @SuppressLint("MissingPermission")
@Override
public void onConnected() {
MyLocationView locationView = userLocationView.get();
@@ -808,6 +813,9 @@ public class MyLocationView extends View {
private Sensor rotationVectorSensor;
private float[] matrix = new float[9];
+ private float[] rotationVectorValue;
+ private float[] truncatedRotationVectorValue = new float[4];
+
private float[] orientation = new float[3];
private boolean reportMissingSensor = true;
// Compass data
@@ -844,9 +852,8 @@ public class MyLocationView extends View {
}
if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
-
- // calculate the rotation matrix
- SensorManager.getRotationMatrixFromVector(matrix, event.values);
+ rotationVectorValue = getRotationVectorFromSensorEvent(event);
+ SensorManager.getRotationMatrixFromVector(matrix, rotationVectorValue);
SensorManager.getOrientation(matrix, orientation);
magneticHeading = (float) Math.toDegrees(SensorManager.getOrientation(matrix, orientation)[0]);
@@ -863,6 +870,28 @@ public class MyLocationView extends View {
}
}
+ /**
+ * Pulls out the rotation vector from a SensorEvent, with a maximum length
+ * vector of four elements to avoid potential compatibility issues.
+ *
+ * @param event the sensor event
+ * @return the events rotation vector, potentially truncated
+ */
+ @NonNull
+ float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) {
+ if (event.values.length > 4) {
+ // On some Samsung devices SensorManager.getRotationMatrixFromVector
+ // appears to throw an exception if rotation vector has length > 4.
+ // For the purposes of this class the first 4 values of the
+ // rotation vector are sufficient (see crbug.com/335298 for details).
+ // Only affects Android 4.3
+ System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4);
+ return truncatedRotationVectorValue;
+ } else {
+ return event.values;
+ }
+ }
+
private void rotateCamera(float rotation) {
CameraPosition.Builder builder = new CameraPosition.Builder();
builder.bearing(rotation);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
index ea74bc57aa..a1d5b13b8b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
@@ -13,7 +13,10 @@ import com.mapbox.mapboxsdk.maps.Projection;
/**
* Settings to configure the visual appearance of the MyLocationView.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
*/
+@Deprecated
public class MyLocationViewSettings {
private Projection projection;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java
index a1bd98b780..817dcdb438 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.net;
+import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -21,6 +22,7 @@ import timber.log.Timber;
* Not public api.
*/
public class ConnectivityReceiver extends BroadcastReceiver {
+ @SuppressLint("StaticFieldLeak")
private static ConnectivityReceiver INSTANCE;
/**
@@ -82,7 +84,7 @@ public class ConnectivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
boolean connected = isConnected(context);
- Timber.v("Connected: " + connected);
+ Timber.v("Connected: %s", connected);
// Loop over listeners
for (ConnectivityListener listener : listeners) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
index 1e6f44f094..130284e88d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.offline;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -40,6 +41,7 @@ public class OfflineManager {
private Handler handler;
// This object is implemented as a singleton
+ @SuppressLint("StaticFieldLeak")
private static OfflineManager instance;
// The application context
@@ -90,11 +92,11 @@ public class OfflineManager {
*/
private OfflineManager(Context context) {
this.context = context.getApplicationContext();
- this.fileSource = FileSource.getInstance(context);
+ this.fileSource = FileSource.getInstance(this.context);
initialize(fileSource);
// Delete any existing previous ambient cache database
- deleteAmbientDatabase(context);
+ deleteAmbientDatabase(this.context);
}
private void deleteAmbientDatabase(final Context context) {
@@ -107,10 +109,10 @@ public class OfflineManager {
File file = new File(path);
if (file.exists()) {
file.delete();
- Timber.d("Old ambient cache database deleted to save space: " + path);
+ Timber.d("Old ambient cache database deleted to save space: %s", path);
}
} catch (Exception exception) {
- Timber.e("Failed to delete old ambient cache database: ", exception);
+ Timber.e(exception, "Failed to delete old ambient cache database: ");
}
}
}).start();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
index 1b8c4121ef..f210729037 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
@@ -358,10 +358,10 @@ public class OfflineRegion {
*/
public void delete(@NonNull final OfflineRegionDeleteCallback callback) {
if (!isDeleted) {
+ isDeleted = true;
deleteOfflineRegion(new OfflineRegionDeleteCallback() {
@Override
public void onDelete() {
- isDeleted = true;
getHandler().post(new Runnable() {
@Override
public void run() {
@@ -376,6 +376,7 @@ public class OfflineRegion {
getHandler().post(new Runnable() {
@Override
public void run() {
+ isDeleted = false;
callback.onError(error);
}
});
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
index fe12dd46c4..0f4b81fc39 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
@@ -80,7 +80,7 @@ public class OfflineRegionStatus {
* @return true if download is complete, false if not
*/
public boolean isComplete() {
- return (completedResourceCount == requiredResourceCount);
+ return (completedResourceCount == requiredResourceCount) && downloadState == OfflineRegion.STATE_INACTIVE;
}
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
index f8ec0f3d39..2a32f0bdd6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
@@ -1,5 +1,9 @@
package com.mapbox.mapboxsdk.offline;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
/**
@@ -13,7 +17,7 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
* <p>
* pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0.
*/
-public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefinition {
+public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefinition, Parcelable {
private String styleURL;
private LatLngBounds bounds;
@@ -22,7 +26,7 @@ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefiniti
private float pixelRatio;
/**
- * Constructor
+ * Constructor to create an OfflineTilePyramidDefinition from parameters.
*
* @param styleURL the style
* @param bounds the bounds
@@ -40,6 +44,22 @@ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefiniti
this.pixelRatio = pixelRatio;
}
+ /**
+ * Constructor to create an OfflineTilePyramidDefinition from a Parcel.
+ *
+ * @param parcel the parcel to create the OfflineTilePyramidDefinition from
+ */
+ public OfflineTilePyramidRegionDefinition(Parcel parcel) {
+ this.styleURL = parcel.readString();
+ this.bounds = new LatLngBounds.Builder()
+ .include(new LatLng(parcel.readDouble(), parcel.readDouble()))
+ .include(new LatLng(parcel.readDouble(), parcel.readDouble()))
+ .build();
+ this.minZoom = parcel.readDouble();
+ this.maxZoom = parcel.readDouble();
+ this.pixelRatio = parcel.readFloat();
+ }
+
/*
* Getters
*/
@@ -64,4 +84,34 @@ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefiniti
return pixelRatio;
}
+ /*
+ * Parceable
+ */
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(styleURL);
+ dest.writeDouble(bounds.getLatNorth());
+ dest.writeDouble(bounds.getLonEast());
+ dest.writeDouble(bounds.getLatSouth());
+ dest.writeDouble(bounds.getLonWest());
+ dest.writeDouble(minZoom);
+ dest.writeDouble(maxZoom);
+ dest.writeFloat(pixelRatio);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ public OfflineTilePyramidRegionDefinition createFromParcel(Parcel in) {
+ return new OfflineTilePyramidRegionDefinition(in);
+ }
+
+ public OfflineTilePyramidRegionDefinition[] newArray(int size) {
+ return new OfflineTilePyramidRegionDefinition[size];
+ }
+ };
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
new file mode 100644
index 0000000000..72df86d80d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
@@ -0,0 +1,260 @@
+package com.mapbox.mapboxsdk.snapshotter;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+
+import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.storage.FileSource;
+
+/**
+ * The map snapshotter creates a bitmap of the map, rendered
+ * off the UI thread. The snapshotter itself must be used on
+ * the UI thread (for access to the main looper)
+ */
+@UiThread
+public class MapSnapshotter {
+
+ /**
+ * Can be used to get notified of errors
+ * in snapshot generation
+ *
+ * @see MapSnapshotter#start(MapboxMap.SnapshotReadyCallback, ErrorHandler)
+ */
+ public interface ErrorHandler {
+
+ /**
+ * Called on error. Snapshotting will not
+ * continue
+ *
+ * @param error the error message
+ */
+ void onError(String error);
+ }
+
+ private static final int LOGO_MARGIN_PX = 4;
+
+ // Holds the pointer to JNI NativeMapView
+ private long nativePtr = 0;
+
+ private final Context context;
+ private MapboxMap.SnapshotReadyCallback callback;
+ private ErrorHandler errorHandler;
+
+ /**
+ * MapSnapshotter options
+ */
+ public static class Options {
+ private int pixelRatio = 1;
+ private int width;
+ private int height;
+ private String styleUrl = Style.MAPBOX_STREETS;
+ private LatLngBounds region;
+ private CameraPosition cameraPosition;
+
+ /**
+ * @param width the width of the image
+ * @param height the height of the image
+ */
+ public Options(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * @param url The style URL to use
+ * @return the mutated {@link Options}
+ */
+ public Options withStyle(String url) {
+ this.styleUrl = url;
+ return this;
+ }
+
+ /**
+ * @param region the region to show in the snapshot.
+ * This is applied after the camera position
+ * @return the mutated {@link Options}
+ */
+ public Options withRegion(LatLngBounds region) {
+ this.region = region;
+ return this;
+ }
+
+ /**
+ * @param pixelRatio the pixel ratio to use (default: 1)
+ * @return the mutated {@link Options}
+ */
+ public Options withPixelRatio(int pixelRatio) {
+ this.pixelRatio = pixelRatio;
+ return this;
+ }
+
+ /**
+ * @param cameraPosition The camera position to use,
+ * the {@link CameraPosition#target} is overridden
+ * by region if set in conjunction.
+ * @return the mutated {@link Options}
+ */
+ public Options withCameraPosition(CameraPosition cameraPosition) {
+ this.cameraPosition = cameraPosition;
+ return this;
+ }
+
+ /**
+ * @return the width of the image
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * @return the height of the image
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * @return the pixel ratio
+ */
+ public int getPixelRatio() {
+ return pixelRatio;
+ }
+
+ /**
+ * @return the region
+ */
+ @Nullable
+ public LatLngBounds getRegion() {
+ return region;
+ }
+
+ /**
+ * @return the style url
+ */
+ public String getStyleUrl() {
+ return styleUrl;
+ }
+
+ /**
+ * @return the camera position
+ */
+ @Nullable
+ public CameraPosition getCameraPosition() {
+ return cameraPosition;
+ }
+ }
+
+ /**
+ * Creates the Map snapshotter, but doesn't start rendering or
+ * loading yet.
+ *
+ * @param context the Context that is or contains the Application context
+ * @param options the options to use for the snapshot
+ */
+ public MapSnapshotter(@NonNull Context context, @NonNull Options options) {
+ this.context = context.getApplicationContext();
+ FileSource fileSource = FileSource.getInstance(context);
+ String programCacheDir = context.getCacheDir().getAbsolutePath();
+
+ nativeInitialize(this, fileSource, options.pixelRatio, options.width,
+ options.height, options.styleUrl, options.region, options.cameraPosition,
+ programCacheDir);
+ }
+
+ /**
+ * Starts loading and rendering the snapshot. The callback will be fired
+ * on the calling thread.
+ *
+ * @param callback the callback to use when the snapshot is ready
+ */
+ public void start(@NonNull MapboxMap.SnapshotReadyCallback callback) {
+ this.start(callback, null);
+ }
+
+ /**
+ * Starts loading and rendering the snapshot. The callbacks will be fired
+ * on the calling thread.
+ *
+ * @param callback the callback to use when the snapshot is ready
+ * @param errorHandler the error handler to use on snapshot errors
+ */
+ public void start(@NonNull MapboxMap.SnapshotReadyCallback callback, ErrorHandler errorHandler) {
+ if (this.callback != null) {
+ throw new IllegalStateException("Snapshotter was already started");
+ }
+
+ this.callback = callback;
+ this.errorHandler = errorHandler;
+ nativeStart();
+ }
+
+ /**
+ * Must be called in on the thread
+ * the object was created on.
+ */
+ public void cancel() {
+ callback = null;
+ nativeCancel();
+ }
+
+ protected void addOverlay(Bitmap original) {
+ float margin = context.getResources().getDisplayMetrics().density * LOGO_MARGIN_PX;
+ Canvas canvas = new Canvas(original);
+ Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, null);
+ canvas.drawBitmap(logo, margin, original.getHeight() - (logo.getHeight() + margin), null);
+ }
+
+ /**
+ * Called by JNI peer when snapshot is ready.
+ * Always called on the origin (main) thread.
+ *
+ * @param bitmap the generated snapshot
+ */
+ protected void onSnapshotReady(Bitmap bitmap) {
+ if (callback != null) {
+ addOverlay(bitmap);
+ callback.onSnapshotReady(bitmap);
+ reset();
+ }
+ }
+
+ /**
+ * Called by JNI peer when snapshot has failed.
+ * Always called on the origin (main) thread.
+ *
+ * @param reason the exception string
+ */
+ protected void onSnapshotFailed(String reason) {
+ if (errorHandler != null) {
+ errorHandler.onError(reason);
+ reset();
+ }
+ }
+
+ protected void reset() {
+ callback = null;
+ errorHandler = null;
+ }
+
+ protected native void nativeInitialize(MapSnapshotter mapSnapshotter,
+ FileSource fileSource, float pixelRatio,
+ int width, int height, String styleUrl,
+ LatLngBounds region, CameraPosition position,
+ String programCacheDir);
+
+ protected native void nativeStart();
+
+ protected native void nativeCancel();
+
+ @Override
+ protected native void finalize() throws Throwable;
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
index eafef80e8d..a968cdf192 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
@@ -72,9 +72,9 @@ public class FileSource {
MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL,
MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL);
} catch (PackageManager.NameNotFoundException exception) {
- Timber.e("Failed to read the package metadata: ", exception);
+ Timber.e(exception,"Failed to read the package metadata: ");
} catch (Exception exception) {
- Timber.e("Failed to read the storage key: ", exception);
+ Timber.e(exception, "Failed to read the storage key: ");
}
String cachePath = null;
@@ -83,7 +83,7 @@ public class FileSource {
// Try getting the external storage path
cachePath = context.getExternalFilesDir(null).getAbsolutePath();
} catch (NullPointerException exception) {
- Timber.e("Failed to obtain the external storage path: ", exception);
+ Timber.e(exception, "Failed to obtain the external storage path: ");
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java
index e1e40821b1..e7bb52ebb3 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java
@@ -279,7 +279,7 @@ public class Function<I, O> {
// noinspection unchecked
return (S) stops;
} catch (ClassCastException exception) {
- Timber.e(String.format("Stops: %s is a different type: %s", stops.getClass(), exception));
+ Timber.e(exception, "Stops: %s is a different type: ", stops.getClass());
return null;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java
index 7807556b78..f77e7280f0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java
@@ -13,7 +13,16 @@ public class CustomLayer extends Layer {
long initializeFunction,
long renderFunction,
long deinitializeFunction) {
- initialize(id, initializeFunction, renderFunction, deinitializeFunction, context);
+ this(id, context, initializeFunction, renderFunction, 0L, deinitializeFunction);
+ }
+
+ public CustomLayer(String id,
+ long context,
+ long initializeFunction,
+ long renderFunction,
+ long contextLostFunction,
+ long deinitializeFunction) {
+ initialize(id, initializeFunction, renderFunction, contextLostFunction, deinitializeFunction, context);
}
public CustomLayer(long nativePtr) {
@@ -24,7 +33,8 @@ public class CustomLayer extends Layer {
nativeUpdate();
}
- protected native void initialize(String id, long initializeFunction, long renderFunction, long deinitializeFunction,
+ protected native void initialize(String id, long initializeFunction, long renderFunction,
+ long contextLostFunction, long deinitializeFunction,
long context);
protected native void nativeUpdate();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
index be24b65d27..8d5858217b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
@@ -160,6 +160,62 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface ICON_TEXT_FIT {}
+ // ICON_ANCHOR: Part of the icon placed closest to the anchor.
+
+ /**
+ * The center of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_CENTER = "center";
+ /**
+ * The left side of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_LEFT = "left";
+ /**
+ * The right side of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_RIGHT = "right";
+ /**
+ * The top of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_TOP = "top";
+ /**
+ * The bottom of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_BOTTOM = "bottom";
+ /**
+ * The top left corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_TOP_LEFT = "top-left";
+ /**
+ * The top right corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_TOP_RIGHT = "top-right";
+ /**
+ * The bottom left corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_BOTTOM_LEFT = "bottom-left";
+ /**
+ * The bottom right corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_BOTTOM_RIGHT = "bottom-right";
+
+ /**
+ * Part of the icon placed closest to the anchor.
+ */
+ @StringDef({
+ ICON_ANCHOR_CENTER,
+ ICON_ANCHOR_LEFT,
+ ICON_ANCHOR_RIGHT,
+ ICON_ANCHOR_TOP,
+ ICON_ANCHOR_BOTTOM,
+ ICON_ANCHOR_TOP_LEFT,
+ ICON_ANCHOR_TOP_RIGHT,
+ ICON_ANCHOR_BOTTOM_LEFT,
+ ICON_ANCHOR_BOTTOM_RIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_ANCHOR {}
+
// ICON_PITCH_ALIGNMENT: Orientation of icon when map is pitched.
/**
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 ef89c6809e..d4ddbe48ef 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
@@ -1882,6 +1882,29 @@ public class PropertyFactory {
}
/**
+ * Part of the icon placed closest to the anchor.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> iconAnchor(@Property.ICON_ANCHOR String value) {
+ return new LayoutPropertyValue<>("icon-anchor", value);
+ }
+
+
+
+ /**
+ * Part of the icon placed closest to the anchor.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for String
+ * @return property wrapper around a String function
+ */
+ public static <T> PropertyValue<Function<T, String>> iconAnchor(Function<T, String> function) {
+ return new LayoutPropertyValue<>("icon-anchor", function);
+ }
+
+ /**
* Orientation of icon when map is pitched.
*
* @param value a String value
@@ -2034,11 +2057,11 @@ public class PropertyFactory {
/**
* The maximum line width for text wrapping.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textMaxWidth(CameraFunction<Z, Float> function) {
+ public static <T> PropertyValue<Function<T, Float>> textMaxWidth(Function<T, Float> function) {
return new LayoutPropertyValue<>("text-max-width", function);
}
@@ -2080,11 +2103,11 @@ public class PropertyFactory {
/**
* Text tracking amount.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textLetterSpacing(CameraFunction<Z, Float> function) {
+ public static <T> PropertyValue<Function<T, Float>> textLetterSpacing(Function<T, Float> function) {
return new LayoutPropertyValue<>("text-letter-spacing", function);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
index fe81dcb638..d0fb82dce5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
@@ -252,6 +252,16 @@ public class SymbolLayer extends Layer {
}
/**
+ * Get the IconAnchor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconAnchor() {
+ return (PropertyValue<String>) new PropertyValue("icon-anchor", nativeGetIconAnchor());
+ }
+
+ /**
* Get the IconPitchAlignment property
*
* @return property wrapper value around String
@@ -901,6 +911,8 @@ public class SymbolLayer extends Layer {
private native Object nativeGetIconOffset();
+ private native Object nativeGetIconAnchor();
+
private native Object nativeGetIconPitchAlignment();
private native Object nativeGetTextPitchAlignment();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
new file mode 100644
index 0000000000..c7d7a13a3d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
@@ -0,0 +1,27 @@
+package com.mapbox.mapboxsdk.utils;
+
+/**
+ * Comparisons from std sdk, which aren't available in API level <= 15
+ */
+public class Compare {
+
+ /**
+ * @see Integer#compare(int, int)
+ * @param x left side
+ * @param y right side
+ * @return std compare value
+ */
+ public static int compare(int x, int y) {
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
+ }
+
+ /**
+ * @see Boolean#compare(boolean, boolean)
+ * @param x left side
+ * @param y right side
+ * @return std compare value
+ */
+ public static int compare(boolean x, boolean y) {
+ return (x == y) ? 0 : (x ? 1 : -1);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
index 3decd6ead1..40045f851f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
@@ -72,9 +72,6 @@
<public name="mapbox_uiAttributionMarginRight" type="attr" />
<public name="mapbox_uiAttributionMarginBottom" type="attr" />
- <!-- Deprecated to use TextureView-->
- <public name="mapbox_renderTextureMode" type="attr" />
-
<public name="mapbox_enableTilePrefetch" type="attr" />
<public name="mapbox_enableZMediaOverlay" type="attr" />
@@ -83,11 +80,13 @@
<!-- Exposed styles -->
<public name="mapbox_style_mapbox_streets" type="string" />
- <public name="mapbox_style_emerald" type="string" />
+ <public name="mapbox_style_outdoors" type="string" />
<public name="mapbox_style_light" type="string" />
<public name="mapbox_style_dark" type="string" />
<public name="mapbox_style_satellite" type="string" />
<public name="mapbox_style_satellite_streets" type="string" />
+ <public name="mapbox_style_traffic_day" type="string" />
+ <public name="mapbox_style_traffic_night" type="string" />
<!-- Exposed strings -->
<public name="mapbox_compassContentDescription" type="string" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml
deleted file mode 100644
index 4c733ed112..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?attr/colorPrimaryDark" android:state_pressed="true" />
- <item android:color="?attr/colorPrimary" />
-</selector>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml
deleted file mode 100644
index 2fd6815c23..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?android:colorControlHighlight">
- <item android:id="@android:id/mask">
- <shape android:shape="oval">
- <solid android:color="@android:color/white" />
- </shape>
- </item>
-</ripple>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpg b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpg
deleted file mode 100644
index 3a21fc08bc..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpg
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png
deleted file mode 100644
index 584b320299..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml
deleted file mode 100644
index 3efe48615b..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/mapbox_gray_light" android:state_pressed="true" />
- <item android:drawable="@color/mapbox_gray_light" android:state_focused="true" />
- <item android:drawable="@android:color/transparent" />
-</selector>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml
index 763bb118e0..f275860d59 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml
@@ -9,5 +9,6 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:textAllCaps="true"
+ android:textIsSelectable="false"
android:textAppearance="?android:attr/textAppearanceButton"
android:textColor="@color/mapbox_blue"/>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml
index 26c974dc0d..3a35396257 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml
@@ -23,6 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
+ android:textIsSelectable="false"
android:maxEms="17"
android:textColor="@android:color/black"
android:textSize="18sp"
@@ -34,13 +35,13 @@
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
+ android:textIsSelectable="false"
android:lineSpacingExtra="1dp"
android:maxEms="17"
android:textColor="@color/mapbox_gray"
android:textSize="14sp"/>
<TextView
- android:id="@+id/infowindow_subdescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxEms="17"
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
index 6d07de7baa..df7ccaaca9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <SurfaceView
+ <android.opengl.GLSurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -28,6 +28,7 @@
android:contentDescription="@string/mapbox_compassContentDescription"/>
<ImageView
+ android:visibility="gone"
android:id="@+id/logoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -35,6 +36,7 @@
android:src="@drawable/mapbox_logo_icon"/>
<ImageView
+ android:visibility="gone"
android:id="@+id/attributionView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml
deleted file mode 100644
index 1c1ab0e71b..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:contentDescription="@null"
- android:src="@drawable/mapbox_mapview_preview"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"/>
-
- <ImageView
- android:id="@id/logoView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_marginBottom="@dimen/mapbox_four_dp"
- android:layout_marginLeft="@dimen/mapbox_four_dp"
- android:layout_marginStart="@dimen/mapbox_four_dp"
- android:contentDescription="@null"
- android:src="@drawable/mapbox_logo_icon"/>
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_toEndOf="@+id/logoView"
- android:contentDescription="@null"
- android:layout_marginBottom="@dimen/mapbox_four_dp"
- android:background="@drawable/mapbox_default_bg_selector"
- android:src="@drawable/mapbox_info_bg_selector"
- android:layout_marginLeft="@dimen/mapbox_four_dp"
- android:layout_marginStart="@dimen/mapbox_four_dp"
- android:layout_toRightOf="@+id/logoView"/>
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_margin="10dp"
- android:contentDescription="@null"
- android:src="@drawable/mapbox_compass_icon"/>
-
-</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml
index 7e4a079063..51eb46e1d5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:id="@+id/image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@null"/>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml
index 34e9914e46..6ed788d1dc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml
@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="mapbox_compassContentDescription">Brúixola del mapa. Activa per restablir la rotació del mapa al Nord.</string>
- <string name="mapbox_attributionsIconContentDescription">Icona d\'atribució. Activa per mostrar el diàleg de l\'atribució.</string>
+ <string name="mapbox_attributionsIconContentDescription">Icona d’atribució. Activa per mostrar el diàleg de l’atribució.</string>
<string name="mapbox_myLocationViewContentDescription">Vista de posició. Mostra la teva posició al mapa.</string>
- <string name="mapbox_mapActionDescription">Mostrant un mapa creat amb Mapbox. Desplaça\'t arrossegant amb dos dits. Fes zoom pessigant amb dos dits.</string>
+ <string name="mapbox_mapActionDescription">Mostrant un mapa creat amb Mapbox. Desplaça’t arrossegant amb dos dits. Fes zoom pessigant amb dos dits.</string>
<string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
<string name="mapbox_attributionTelemetryTitle">Millora els mapes de Mapbox</string>
- <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d\'OpenStreetMap i de Mapbox aportant dades d\'ús anònimes.</string>
- <string name="mapbox_attributionTelemetryPositive">D\'acord</string>
+ <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d’OpenStreetMap i de Mapbox aportant dades d’ús anònimes.</string>
+ <string name="mapbox_attributionTelemetryPositive">D’acord</string>
<string name="mapbox_attributionTelemetryNegative">Disconforme</string>
<string name="mapbox_attributionTelemetryNeutral">Més informació</string>
<string name="mapbox_offline_error_region_definition_invalid">La OfflineRegionDefinition proporcionada no encaixa amb els límits del món: %s</string>
-</resources>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000000..48d90c3324
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="mapbox_compassContentDescription">Boussole. Activer pour rétablir l\'orientation de la carte vers le nord.</string>
+ <string name="mapbox_attributionsIconContentDescription">Icone d\'attribution. Activer pour montrer le dialogue d\'attribution.</string>
+ <string name="mapbox_myLocationViewContentDescription">Vue de géolocalisation. Ceci affiche votre localisation sur la carte.</string>
+ <string name="mapbox_mapActionDescription">Affichage d\'une carte créée avec Mapbox. Faites la glisser en traînant deux doigts. Zoomez ou dézoomez en écartant ou rapprochant deux doigts.</string>
+ <string name="mapbox_attributionsDialogTitle">SDK Mapbox pour Android</string>
+ <string name="mapbox_attributionTelemetryTitle">Faire de meilleures cartes Mapbox</string>
+ <string name="mapbox_attributionTelemetryMessage">Vous aidez à améliorer les cartes OpenStreetMap et Mapbox en contribuant des données d\'utilisation anonymes.</string>
+ <string name="mapbox_attributionTelemetryPositive">D\'accord</string>
+ <string name="mapbox_attributionTelemetryNegative">Pas d\'accord</string>
+ <string name="mapbox_attributionTelemetryNeutral">Plus d\'informations</string>
+ <string name="mapbox_offline_error_region_definition_invalid">Le cadre OfflineRegionDefinition pour définir la région de navigation ne tient pas dans les limites du monde : %s</string>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml
index fef652c542..f1fcf837ea 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml
@@ -11,4 +11,5 @@
<string name="mapbox_attributionTelemetryNegative">Intrekken</string>
<string name="mapbox_attributionTelemetryNeutral">Meer informatie</string>
<string name="mapbox_offline_error_region_definition_invalid">Aangeleverde OfflineRegionDefinition past niet in de wereld omtrek: %s</string>
-</resources>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000000..39880d56ba
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="mapbox_compassContentDescription">Компас. Активируйте, чтобы повернуть карту на Север.</string>
+ <string name="mapbox_attributionsIconContentDescription">Иконка атрибутов. Активируйте, чтобы показать диалог.</string>
+ <string name="mapbox_myLocationViewContentDescription">Местоположение. Отображает ваше местоположение на карте.</string>
+ <string name="mapbox_mapActionDescription">Отображает карту, созданную при помощи Mapbox. Протисните при помощи двух пальцев. Приблизьте, соединением пальцев. </string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionTelemetryTitle">Сделать карты Mapbox лучше</string>
+ <string name="mapbox_attributionTelemetryMessage">Вы помогаете сделать карты OpenStreetMap и Mapbox лучше путем предоставления анонимных данных об использовании.</string>
+ <string name="mapbox_attributionTelemetryPositive">Согласен</string>
+ <string name="mapbox_attributionTelemetryNegative">Не согласен</string>
+ <string name="mapbox_attributionTelemetryNeutral">Дополнительная информация</string>
+ <string name="mapbox_offline_error_region_definition_invalid">Запрошенный OfflineRegionDefinition не входит в допустимые границы: %s</string>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml
new file mode 100644
index 0000000000..64c7e3efcc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="mapbox_compassContentDescription">Компас. Натисніть щоб зорієнтувати мапу на північ.</string>
+ <string name="mapbox_attributionsIconContentDescription">Значок атрибуції. Натисніть, щоб показати діалог атрибуції.</string>
+ <string name="mapbox_myLocationViewContentDescription">Визначення положення. Показує ваше місцеположення на мапі.</string>
+ <string name="mapbox_mapActionDescription">Показує мапи створені за допомоги Mapbox. Пересувайте мапу двома пальцями. Змінюйте масштаб стуляючи/розводячи два пальці.</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionTelemetryTitle">Допоможіть зробити мапи Mapbox краще</string>
+ <string name="mapbox_attributionTelemetryMessage">Ви допомагаєте робити мапи OpenStreetMap та Mapbox краще пощирюючи анонімні дані про користування мапами.</string>
+ <string name="mapbox_attributionTelemetryPositive">Погоджуюсь</string>
+ <string name="mapbox_attributionTelemetryNegative">Не погоджуюсь</string>
+ <string name="mapbox_attributionTelemetryNeutral">Додаткова інформація</string>
+ <string name="mapbox_offline_error_region_definition_invalid">Межі ділянки для оффлайнового користування даними за межами світу: %s</string>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
index 281fe8afe3..b673224094 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -114,9 +114,6 @@
<attr name="mapbox_uiAttributionMarginBottom" format="dimension"/>
<attr name="mapbox_uiAttributionTintColor" format="color"/>
- <!-- Deprecated to use TextureView-->
- <attr name="mapbox_renderTextureMode" format="boolean"/>
-
<attr name="mapbox_enableTilePrefetch" format="boolean"/>
<attr name="mapbox_enableZMediaOverlay" format="boolean"/>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
index 63ef42c2c3..b51c890e5c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="mapbox_gray">#7D7F80</color>
- <color name="mapbox_gray_light">#EEEEEE</color>
<color name="mapbox_blue">#1E8CAB</color>
- <color name="mapbox_my_location_ring">@color/mapbox_blue</color>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index 8edbd47c29..1c6a265587 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -2,17 +2,8 @@
<resources>
<dimen name="mapbox_infowindow_tipview_width">8dp</dimen>
<dimen name="mapbox_infowindow_margin">8dp</dimen>
- <dimen name="mapbox_infowindow_offset">-2dp</dimen>
- <dimen name="mapbox_infowindow_line_width">1.5dp</dimen>
- <dimen name="mapbox_attribution_icon_left_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_attribution_icon_top_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_attribution_icon_right_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_attribution_icon_bottom_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_two_dp">2dp</dimen>
<dimen name="mapbox_four_dp">4dp</dimen>
<dimen name="mapbox_eight_dp">8dp</dimen>
- <dimen name="mapbox_ten_dp">10dp</dimen>
- <dimen name="mapbox_sixteen_dp">16dp</dimen>
<dimen name="mapbox_ninety_two_dp">92dp</dimen>
<dimen name="mapbox_my_locationview_outer_circle">18dp</dimen>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties b/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties
index 029d25e046..716f0ffe70 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties
+++ b/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties
@@ -1,3 +1,3 @@
fabric-identifier=com.mapbox.mapboxsdk.mapbox-android-sdk
-fabric-version=5.1.0
+fabric-version=5.1.4
fabric-build-type=binary
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index b13362751a..67939b5144 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -25,12 +25,11 @@ android {
}
lintOptions {
- baseline file("lint-baseline.xml")
+ baseline file("lint-baseline-local.xml")
checkAllWarnings true
warningsAsErrors true
- disable 'MissingTranslation'
- disable 'IconDensities'
- disable 'InvalidPackage'
+ disable 'MissingTranslation', 'GoogleAppIndexingWarning', 'UnpackedNativeCode', 'IconDipSize', 'TypographyQuotes'
+ abortOnError false
}
buildTypes {
@@ -64,7 +63,6 @@ dependencies {
// Leak Canary
debugCompile rootProject.ext.dep.leakCanaryDebug
releaseCompile rootProject.ext.dep.leakCanaryRelease
- testCompile rootProject.ext.dep.leakCanaryTest
// Mapbox Android Services (Java component)
compile(rootProject.ext.dep.mapboxJavaServices) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml
new file mode 100644
index 0000000000..e3c5abce4f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 2.3.1">
+
+ <issue
+ id="UnusedResources"
+ message="The resource `R.string.mapbox_access_token` appears to be unused"
+ errorLine1=" &lt;string name=&quot;mapbox_access_token&quot;>YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE&lt;/string>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/values/developer-config.xml"
+ line="3"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `#cccc` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
+ errorLine1=" android:background=&quot;#cccc&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/layout/drawer_navigation_drawer.xml"
+ line="4"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
+ errorLine1=" android:background=&quot;?android:attr/selectableItemBackground&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/layout/item_main_feature.xml"
+ line="6"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `@color/mapboxGreen` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
+ errorLine1=" android:background=&quot;@color/mapboxGreen&quot;>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/layout/view_text_marker.xml"
+ line="5"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="TypographyQuotes"
+ message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?"
+ errorLine1=" &lt;string name=&quot;mapbox_attributionTelemetryMessage&quot;>Estàs ajudant a millorar els mapes d\&apos;OpenStreetMap i de Mapbox aportant dades d\&apos;ús anònimes.&lt;/string>"
+ errorLine2=" ^">
+ <location
+ file="src/main/res/values-ca/strings.xml"
+ line="9"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="IconDuplicatesConfig"
+ message="The `icon_burned.png` icon has identical contents in the following configuration folders: drawable-hdpi, drawable-xhdpi">
+ <location
+ file="src/main/res/drawable-xhdpi/icon_burned.png"/>
+ <location
+ file="src/main/res/drawable-hdpi/icon_burned.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-hdpi/ic_launcher_round.png`: expected 72x72, but was 216x216">
+ <location
+ file="src/main/res/drawable-hdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-mdpi/ic_launcher_round.png`: expected 48x48, but was 144x144">
+ <location
+ file="src/main/res/drawable-mdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xhdpi/ic_launcher_round.png`: expected 96x96, but was 288x288">
+ <location
+ file="src/main/res/drawable-xhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxhdpi/ic_launcher_round.png`: expected 144x144, but was 432x432">
+ <location
+ file="src/main/res/drawable-xxhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxxhdpi/ic_launcher_round.png`: expected 192x192, but was 576x576">
+ <location
+ file="src/main/res/drawable-xxxhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-hdpi/icon.png`: expected 72x72, but was 215x212">
+ <location
+ file="src/main/res/drawable-hdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-mdpi/icon.png`: expected 48x48, but was 143x141">
+ <location
+ file="src/main/res/drawable-mdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xhdpi/icon.png`: expected 96x96, but was 286x282">
+ <location
+ file="src/main/res/drawable-xhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxhdpi/icon.png`: expected 144x144, but was 429x423">
+ <location
+ file="src/main/res/drawable-xxhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxxhdpi/icon.png`: expected 192x192, but was 572x564">
+ <location
+ file="src/main/res/drawable-xxxhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-hdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
+ <location
+ file="src/main/res/drawable-hdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-mdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png">
+ <location
+ file="src/main/res/drawable-mdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xhdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
+ <location
+ file="src/main/res/drawable-xhdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xxhdpi`: ic_car_top.png, ic_taxi_top.png, southeast_radar_0.png, southeast_radar_1.png, southeast_radar_2.png... (1 more)">
+ <location
+ file="src/main/res/drawable-xxhdpi"/>
+ </issue>
+
+</issues>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml
index 89eab70c48..64e3d41bcc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml
@@ -1,1418 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- REMEMBER! First you run Lint locally you'll need to move the lint-baseline.xml file
+<!-- REMEMBER! First you run Lint locally you'll need to move lint-baseline-local.xml.xml file
generated into the lint folder and called it lint-baseline-local.xml
If you remove any error when running Lint locally, you'll get a warning from the command
line advising you to remove it from the baseline. If you remove it (remember to remove it
from lint-baseline-local.xml file) you should remove it too from
lint-baseline-ci.xml (THIS FILE) which is the only one included in the repo.
Eventually, it'll be removed (when we remove all current lint errors included). -->
-<issues format="4" by="lint 2.3.1">
-
- <issue
- id="MissingPermission"
- message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
- errorLine1=" Location lastKnownLocation = locationEngine.getLastLocation();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java"
- line="783"
- column="38"/>
- </issue>
-
- <issue
- id="MissingPermission"
- message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
- errorLine1=" locationEngine.requestLocationUpdates();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java"
- line="787"
- column="9"/>
- </issue>
-
- <issue
- id="MissingPermission"
- message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
- errorLine1=" .target(new LatLng(Mapbox.getLocationSource().getLastLocation()))"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java"
- line="107"
- column="34"/>
- </issue>
-
- <issue
- id="DefaultLocale"
- message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
- errorLine1=" ((TextView) infoWindow.getView()).setText(String.format(&quot;%.2fkm&quot;, distanceKm));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java"
- line="72"
- column="53"/>
- </issue>
-
- <issue
- id="DefaultLocale"
- message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
- errorLine1=" Timber.v(String.format(&quot;[HTTP] Request was successful (code = %d).&quot;, response.code()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="112"
- column="16"/>
- </issue>
-
- <issue
- id="DefaultLocale"
- message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
- errorLine1=" Timber.d(String.format("
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="116"
- column="16"/>
- </issue>
-
- <issue
- id="DefaultLocale"
- message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
- errorLine1=" String.format(&quot;Snapshot taken in %d ms&quot;, duration),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java"
- line="59"
- column="11"/>
- </issue>
-
- <issue
- id="InflateParams"
- message="Avoid passing `null` as the view root (needed to resolve layout parameters on the inflated layout&apos;s root element)"
- errorLine1=" final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java"
- line="89"
- column="106"/>
- </issue>
-
- <issue
- id="StringFormatMatches"
- message="Suspicious argument type for formatting argument #1 in `debug_zoom`: conversion is `s`, received `double` (argument #2 in method call) (Did you mean formatting character `e`, &apos;f&apos;, &apos;g&apos; or `a`?)"
- errorLine1=" textView.setText(String.format(getString(R.string.debug_zoom), position.zoom));"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java"
- line="60"
- column="76"/>
- <location
- file="src/main/res/values/strings.xml"
- line="186"
- column="5"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Could not add markers,` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Could not add markers,&quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java"
- line="277"
- column="9"/>
- </issue>
-
+<issues by="lint 2.3.1" format="4">
+
<issue
- id="TimberArgCount"
- message="Wrong argument count, format string `That&apos;s not an url... ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;That&apos;s not an url... &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java"
- line="77"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `That&apos;s not an url... ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;That&apos;s not an url... &quot;, malformedUrlException);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java"
- line="135"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Could not resolve package info` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Could not resolve package info&quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java"
- line="95"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to read the package metadata: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to read the package metadata: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="75"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to read the storage key: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to read the storage key: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="77"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to obtain the external storage path: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to obtain the external storage path: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="86"
- column="9"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Failed to delete old ambient cache database: ` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Failed to delete old ambient cache database: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="113"
- column="11"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `could not reflect` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;could not reflect&quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java"
- line="49"
- column="7"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Got NULL feature %s` requires `1` but format call supplies `0`"
- errorLine1=" Timber.i(&quot;Got NULL feature %s&quot;);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java"
- line="84"
- column="9"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Got NULL feature %s` requires `1` but format call supplies `0`"
- errorLine1=" Timber.i(&quot;Got NULL feature %s&quot;);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java"
- line="102"
- column="9"/>
- </issue>
-
- <issue
- id="TimberArgCount"
- message="Wrong argument count, format string `Invalid URL` requires `0` but format call supplies `1`"
- errorLine1=" Timber.e(&quot;Invalid URL&quot;, malformedUrlException);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java"
- line="58"
- column="7"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Remove marker with &quot; + activeMarker.getId());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java"
- line="118"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;showLowThresholdMarker() &quot; + activeMarker.getId());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java"
- line="125"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Remove marker with &quot; + activeMarker.getId());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java"
- line="137"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;showHighThresholdMarker() &quot; + activeMarker.getId());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java"
- line="144"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.v(&quot;Connected: &quot; + connected);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java"
- line="85"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Debug FAB: isDebug Active? &quot; + mapboxMap.isDebugActive());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java"
- line="72"
- column="20"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;That&apos;s not an url... &quot; + malformedUrlException.getMessage());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java"
- line="131"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Device returned an out of range width size, &quot;"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java"
- line="178"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Device returned an out of range height size, &quot;"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java"
- line="185"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Error: &quot; + error);"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java"
- line="210"
- column="18"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Download started: &quot; + regionName);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java"
- line="226"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Offline region created: &quot; + regionName);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java"
- line="244"
- column="18"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Error: &quot; + error);"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java"
- line="251"
- column="18"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Selected item: &quot; + which);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java"
- line="35"
- column="20"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Old ambient cache database deleted to save space: &quot; + path);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="110"
- column="22"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Failed to encode metadata: &quot; + exception.getMessage());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java"
- line="32"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;isMapboxReady called with value &quot; + (mapboxMap != null));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java"
- line="46"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Could not load geojson: &quot; + ioException.getMessage());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java"
- line="62"
- column="20"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Fill anti alias: &quot; + fillAntialias.getValue());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java"
- line="320"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Fill translate anchor: &quot; + fillTranslateAnchor.getValue());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java"
- line="323"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Visibility: &quot; + visibility.getValue());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java"
- line="325"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Fill color base: &quot; + stops.getBase());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java"
- line="480"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Fill color #stops: &quot; + stops.size());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java"
- line="481"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(&quot;Fill color #stops: &quot; + stop);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java"
- line="484"
- column="20"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.i(&quot;Writing style file to: &quot; + cacheStyleFile.getAbsolutePath());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java"
- line="75"
- column="18"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(label + &quot;: begin&quot;);"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java"
- line="149"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(label + &quot;: &quot; + (now - prev) + &quot; ms, &quot; + splitLabel);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java"
- line="156"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.d(label + &quot;: end, &quot; + (now - first) + &quot; ms&quot;);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java"
- line="158"
- column="14"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Not setting minZoomPreference, value is in unsupported range: &quot; + minZoom);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/Transform.java"
- line="349"
- column="16"/>
- </issue>
-
- <issue
- id="BinaryOperationInTimber"
- message="Replace String concatenation with Timber&apos;s string formatting"
- errorLine1=" Timber.e(&quot;Not setting maxZoomPreference, value is in unsupported range: &quot; + maxZoom);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/Transform.java"
- line="361"
- column="16"/>
- </issue>
-
- <issue
- id="LogNotTimber"
- message="Using &apos;Log&apos; instead of &apos;Timber&apos;"
- errorLine1=" Log.w(LOG_TAG, ACCESS_TOKEN_NOT_SET_MESSAGE);"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java"
- line="58"
- column="7"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(&quot;Stops: %s is a different type: %s&quot;, stops.getClass(), exception));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java"
- line="282"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.v(String.format(&quot;[HTTP] Request was successful (code = %d).&quot;, response.code()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="112"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.d(String.format("
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="116"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="163"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="166"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.w(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java"
- line="170"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(&quot;Layer: %s is a different type: %s&quot;, layerId, exception));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java"
- line="286"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(&quot;Source: %s is a different type: %s&quot;, sourceId, exception));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java"
- line="407"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Querying box %s&quot;, box));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java"
- line="56"
- column="14"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java"
- line="92"
- column="16"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java"
- line="78"
- column="14"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Querying box %s&quot;, box));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java"
- line="51"
- column="22"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Got %s features&quot;, features.size()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java"
- line="69"
- column="14"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Got feature %s with %s properties and Geometry %s&quot;,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java"
- line="72"
- column="18"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Prop %s - %s&quot;, entry.getKey(), entry.getValue()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java"
- line="79"
- column="22"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Querying box %s for buildings&quot;, box));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java"
- line="56"
- column="22"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Querying box %s&quot;, box));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java"
- line="79"
- column="22"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format("
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java"
- line="62"
- column="22"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Got %s features&quot;, features.size()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java"
- line="87"
- column="14"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Got feature %s with %s properties and Geometry %s&quot;,"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java"
- line="90"
- column="18"/>
- </issue>
-
- <issue
- id="StringFormatInTimber"
- message="Using &apos;String#format&apos; inside of &apos;Timber&apos;"
- errorLine1=" Timber.i(String.format(&quot;Prop %s - %s&quot;, entry.getKey(), entry.getValue()));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java"
- line="97"
- column="22"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Could not add markers,&quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java"
- line="277"
- column="9"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;That&apos;s not an url... &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java"
- line="77"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;That&apos;s not an url... &quot;, malformedUrlException);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java"
- line="135"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Could not resolve package info&quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java"
- line="95"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to read the package metadata: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="75"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to read the storage key: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="77"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to obtain the external storage path: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java"
- line="86"
- column="9"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Failed to delete old ambient cache database: &quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="113"
- column="11"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;could not reflect&quot;, exception);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java"
- line="49"
- column="7"/>
- </issue>
-
- <issue
- id="ThrowableNotAtBeginning"
- message="Throwable should be first argument"
- errorLine1=" Timber.e(&quot;Invalid URL&quot;, malformedUrlException);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java"
- line="58"
- column="7"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `ConnectivityReceiver` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" private static ConnectivityReceiver INSTANCE;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java"
- line="24"
- column="3"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `IconFactory` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" private static IconFactory instance;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java"
- line="38"
- column="3"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `Mapbox` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" private static Mapbox INSTANCE;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/Mapbox.java"
- line="29"
- column="3"/>
- </issue>
-
- <issue
- id="StaticFieldLeak"
- message="Do not place Android context classes in static fields (static reference to `OfflineManager` which has field `context` pointing to `Context`); this is a memory leak (and also breaks Instant Run)"
- errorLine1=" // This object is implemented as a singleton"
- errorLine2=" ^">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java"
- line="42"
- column="3"/>
- </issue>
-
- <issue
- id="UnpackedNativeCode"
- message="Missing attribute android:extractNativeLibs=&quot;false&quot; on the `&lt;application>` tag."
- errorLine1=" &lt;application"
- errorLine2=" ^">
- <location
- file="src/main/AndroidManifest.xml"
- line="8"
- column="5"/>
- </issue>
-
- <issue
- id="MergeRootFrame"
- message="This `&lt;FrameLayout>` can be replaced with a `&lt;merge>` tag"
- errorLine1="&lt;FrameLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
- errorLine2="^">
- <location
- file="src/main/res/layout/activity_building_layer.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="MergeRootFrame"
- message="This `&lt;FrameLayout>` can be replaced with a `&lt;merge>` tag"
- errorLine1="&lt;FrameLayout"
- errorLine2="^">
- <location
- file="src/main/res/layout/activity_map_in_dialog.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="MergeRootFrame"
- message="This `&lt;FrameLayout>` can be replaced with a `&lt;merge>` tag"
- errorLine1="&lt;FrameLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
- errorLine2="^">
- <location
- file="src/main/res/layout/activity_map_padding.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="DisableBaselineAlignment"
- message="Set `android:baselineAligned=&quot;false&quot;` on this element for better performance"
- errorLine1=" &lt;LinearLayout"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_multi_map.xml"
- line="10"
- column="5"/>
- </issue>
-
- <issue
- id="DisableBaselineAlignment"
- message="Set `android:baselineAligned=&quot;false&quot;` on this element for better performance"
- errorLine1=" &lt;LinearLayout"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_multi_map.xml"
- line="42"
- column="5"/>
- </issue>
-
- <issue
- id="NestedWeights"
- message="Nested weights are bad for performance"
- errorLine1=" android:layout_weight=&quot;0.5&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_multi_map.xml"
- line="23"
- column="13"/>
- </issue>
-
- <issue
- id="NestedWeights"
- message="Nested weights are bad for performance"
- errorLine1=" android:layout_weight=&quot;0.5&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_multi_map.xml"
- line="55"
- column="13"/>
- </issue>
-
- <issue
- id="Overdraw"
- message="Possible overdraw: Root element paints background `#cccc` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
errorLine1=" android:background=&quot;#cccc&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `#cccc` with a theme that also paints a background (inferred theme is `@style/AppTheme`)">
<location
+ column="5"
file="src/main/res/layout/drawer_navigation_drawer.xml"
- line="4"
- column="5"/>
+ line="4"/>
</issue>
<issue
- id="Overdraw"
- message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
errorLine1=" android:background=&quot;?android:attr/selectableItemBackground&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/AppTheme`)">
<location
+ column="5"
file="src/main/res/layout/item_main_feature.xml"
- line="6"
- column="5"/>
+ line="6"/>
</issue>
<issue
- id="Overdraw"
- message="Possible overdraw: Root element paints background `@color/mapboxGreen` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
errorLine1=" android:background=&quot;@color/mapboxGreen&quot;>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `@color/mapboxGreen` with a theme that also paints a background (inferred theme is `@style/AppTheme`)">
<location
+ column="5"
file="src/main/res/layout/view_text_marker.xml"
- line="5"
- column="5"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.color.mapbox_my_location_ring` appears to be unused"
- errorLine1=" &lt;color name=&quot;mapbox_my_location_ring&quot;>@color/mapbox_blue&lt;/color>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml"
- line="6"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.string.mapbox_access_token` appears to be unused"
- errorLine1=" &lt;string name=&quot;mapbox_access_token&quot;>YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/developer-config.xml"
- line="3"
- column="13"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_infowindow_offset` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_infowindow_offset&quot;>-2dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="5"
- column="12"/>
+ line="5"/>
</issue>
<issue
- id="UnusedResources"
- message="The resource `R.dimen.coordinatebounds_margin` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;coordinatebounds_margin&quot;>32dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/values/dimens.xml"
- line="6"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_infowindow_line_width` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_infowindow_line_width&quot;>1.5dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="6"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_left_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_left_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="7"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_top_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_top_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="8"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_right_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_right_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="9"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_attribution_icon_bottom_padding` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_attribution_icon_bottom_padding&quot;>@dimen/mapbox_two_dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="10"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_two_dp` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_two_dp&quot;>2dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="11"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_ten_dp` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_ten_dp&quot;>10dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="14"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.dimen.mapbox_sixteen_dp` appears to be unused"
- errorLine1=" &lt;dimen name=&quot;mapbox_sixteen_dp&quot;>16dp&lt;/dimen>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml"
- line="15"
- column="12"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.drawable.ic_check_box` appears to be unused"
- errorLine1="&lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
- errorLine2="^">
- <location
- file="src/main/res/drawable/ic_check_box.xml"
- line="1"
- column="1"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.drawable.mapbox_infowindow_icon_bg` appears to be unused">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.color.mapbox_material_bg_selector` appears to be unused"
- errorLine1="&lt;selector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;>"
- errorLine2="^">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.drawable.marker` appears to be unused"
- errorLine1="&lt;shape xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; android:shape=&quot;rectangle&quot;>"
- errorLine2="^">
- <location
- file="src/main/res/drawable/marker.xml"
- line="1"
- column="1"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.string.mapbox_style_outdoors` appears to be unused"
- errorLine1=" &lt;string name=&quot;mapbox_style_outdoors&quot; translatable=&quot;false&quot;>mapbox://styles/mapbox/outdoors-v10&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml"
- line="22"
- column="13"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.string.mapbox_style_traffic_day` appears to be unused"
- errorLine1=" &lt;string name=&quot;mapbox_style_traffic_day&quot; translatable=&quot;false&quot;>mapbox://styles/mapbox/traffic-day-v2&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml"
- line="27"
- column="13"/>
- </issue>
-
- <issue
- id="UnusedResources"
- message="The resource `R.string.mapbox_style_traffic_night` appears to be unused"
- errorLine1=" &lt;string name=&quot;mapbox_style_traffic_night&quot; translatable=&quot;false&quot;>mapbox://styles/mapbox/traffic-night-v2&lt;/string>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml"
- line="28"
- column="13"/>
- </issue>
-
- <issue
- id="UselessParent"
- message="This `FrameLayout` layout or its `LinearLayout` parent is possibly useless"
- errorLine1=" &lt;FrameLayout"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_my_location_customization.xml"
- line="9"
- column="5"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.fabStartStop` appears to be unused"
- errorLine1=" android:id=&quot;@+id/fabStartStop&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_animated_image_source.xml"
- line="18"
- column="5"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.map_container` appears to be unused"
- errorLine1=" android:id=&quot;@+id/map_container&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_multi_map.xml"
- line="5"
- column="5"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.map_container1` appears to be unused"
- errorLine1=" android:id=&quot;@+id/map_container1&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_multi_map.xml"
- line="11"
- column="9"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.map_container2` appears to be unused"
- errorLine1=" android:id=&quot;@+id/map_container2&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_multi_map.xml"
- line="43"
- column="9"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.content_frame` appears to be unused"
- errorLine1=" android:id=&quot;@+id/content_frame&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_scroll_by.xml"
- line="69"
- column="9"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.viewpager_header` appears to be unused"
- errorLine1=" android:id=&quot;@+id/viewpager_header&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_viewpager.xml"
- line="13"
- column="13"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.map_card` appears to be unused"
- errorLine1=" android:id=&quot;@+id/map_card&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/fragment_double_map.xml"
- line="12"
- column="9"/>
- </issue>
-
- <issue
- id="UnusedIds"
- message="The resource `R.id.infowindow_subdescription` appears to be unused"
- errorLine1=" android:id=&quot;@+id/infowindow_subdescription&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="/src/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml"
- line="43"
- column="9"/>
- </issue>
-
- <issue
- id="TypographyQuotes"
- message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?"
errorLine1=" &lt;string name=&quot;mapbox_attributionTelemetryMessage&quot;>Estàs ajudant a millorar els mapes d\&apos;OpenStreetMap i de Mapbox aportant dades d\&apos;ús anònimes.&lt;/string>"
- errorLine2=" ^">
+ errorLine2=" ^"
+ id="TypographyQuotes"
+ message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?">
<location
+ column="55"
file="src/main/res/values-ca/strings.xml"
- line="9"
- column="55"/>
+ line="9"/>
</issue>
<issue
id="IconDipSize"
- message="The image `icon_burned.png` varies significantly in its density-independent (dip) size across the various density versions: drawable-hdpi/icon_burned.png: 64x64 dp (96x96 px), drawable-xxxhdpi/icon_burned.png: 48x48 dp (192x192 px), drawable-xxhdpi/icon_burned.png: 48x48 dp (144x144 px), drawable-mdpi/icon_burned.png: 48x48 dp (48x48 px), drawable-xhdpi/icon_burned.png: 48x48 dp (96x96 px)">
- <location
- file="src/main/res/drawable-xhdpi/icon_burned.png"/>
+ message="The image `icon_burned.png` varies significantly in its density-independent (dip) size across the various density versions: drawable-hdpi/icon_burned.png: 64x64 dp (96x96 px), drawable-xxxhdpi/icon_burned.png: 48x48 dp (192x192 px), drawable-xxhdpi/icon_burned.png: 48x48 dp (144x144 px), drawable-xhdpi/icon_burned.png: 48x48 dp (96x96 px), drawable-mdpi/icon_burned.png: 48x48 dp (48x48 px)">
<location
file="src/main/res/drawable-mdpi/icon_burned.png"/>
<location
+ file="src/main/res/drawable-xhdpi/icon_burned.png"/>
+ <location
file="src/main/res/drawable-xxhdpi/icon_burned.png"/>
<location
file="src/main/res/drawable-xxxhdpi/icon_burned.png"/>
@@ -1500,1070 +147,31 @@
</issue>
<issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_debug_mode.xml"
- line="18"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_marker_bulk.xml"
- line="18"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_marker_view.xml"
- line="18"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/item_main_feature.xml"
- line="14"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/item_main_feature.xml"
- line="25"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1="&lt;TextView"
- errorLine2="^">
- <location
- file="src/main/res/layout/mapbox_attribution_list_item.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/mapbox_infowindow_content.xml"
- line="21"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/mapbox_infowindow_content.xml"
- line="31"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/section_main_layout.xml"
- line="13"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/view_custom_marker.xml"
- line="11"
- column="5"/>
- </issue>
-
- <issue
- id="SelectableText"
- message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
- errorLine1=" &lt;TextView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/view_text_marker.xml"
- line="7"
- column="5"/>
- </issue>
-
- <issue
- id="ButtonStyle"
- message="Buttons in button bars should be borderless; use `style=&quot;?android:attr/buttonBarButtonStyle&quot;` (and `?android:attr/buttonBarStyle` on the parent)"
- errorLine1=" &lt;Button"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_camera_animation_types.xml"
- line="24"
- column="9"/>
- </issue>
-
- <issue
- id="ButtonStyle"
- message="Buttons in button bars should be borderless; use `style=&quot;?android:attr/buttonBarButtonStyle&quot;` (and `?android:attr/buttonBarStyle` on the parent)"
- errorLine1=" &lt;Button"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_camera_animation_types.xml"
- line="31"
- column="9"/>
- </issue>
-
- <issue
- id="ButtonStyle"
- message="Buttons in button bars should be borderless; use `style=&quot;?android:attr/buttonBarButtonStyle&quot;` (and `?android:attr/buttonBarStyle` on the parent)"
- errorLine1=" &lt;Button"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_camera_animation_types.xml"
- line="38"
- column="9"/>
- </issue>
-
- <issue
- id="GoogleAppIndexingWarning"
- message="App is not indexable by Google Search; consider adding at least one Activity with an ACTION-VIEW intent filter. See issue explanation for more details."
- errorLine1=" &lt;application"
- errorLine2=" ^">
- <location
- file="src/main/AndroidManifest.xml"
- line="8"
- column="5"/>
- </issue>
-
- <issue
- id="ContentDescription"
- message="[Accessibility] Missing `contentDescription` attribute on image"
- errorLine1=" &lt;ImageView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_snapshot.xml"
- line="14"
- column="9"/>
- </issue>
-
- <issue
- id="ContentDescription"
- message="[Accessibility] Missing `contentDescription` attribute on image"
- errorLine1="&lt;ImageView xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
- errorLine2="^">
- <location
- file="src/main/res/layout/mapbox_view_image_marker.xml"
- line="2"
- column="1"/>
- </issue>
-
- <issue
- id="ContentDescription"
- message="[Accessibility] Missing `contentDescription` attribute on image"
- errorLine1=" &lt;ImageView"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/view_custom_marker.xml"
- line="6"
- column="5"/>
- </issue>
-
- <issue
- id="SetTextI18n"
- message="String literal in `setText` can not be translated. Use Android resources instead."
- errorLine1=" textView.setText(&quot;Click the map to calculate the distance&quot;);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java"
- line="103"
- column="26"/>
- </issue>
-
- <issue
- id="SetTextI18n"
- message="Do not concatenate text displayed with `setText`. Use resource string with placeholders."
- errorLine1=" viewCountView.setText(&quot;ViewCache size &quot; + markerViewContainer.getChildCount());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java"
- line="153"
- column="39"/>
- </issue>
-
- <issue
- id="SetTextI18n"
- message="String literal in `setText` can not be translated. Use Android resources instead."
- errorLine1=" viewCountView.setText(&quot;ViewCache size &quot; + markerViewContainer.getChildCount());"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java"
- line="153"
- column="39"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;No Results&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;No Results&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_metadata_update.xml"
- line="18"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;No Results&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;No Results&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_offline_region_delete.xml"
- line="18"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Move the map by x/y pixels&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;Move the map by x/y pixels&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_scroll_by.xml"
- line="25"
- column="17"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;X: 0000&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;X: 0000&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_scroll_by.xml"
- line="35"
- column="17"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Y: 0000&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;Y: 0000&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_scroll_by.xml"
- line="51"
- column="17"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Latitude&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;Latitude&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="19"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;-180&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;-180&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="41"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Longitude&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;Longitude&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="59"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;-180&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;-180&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="81"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Zoom&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;Zoom&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="98"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;18&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;18&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="120"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Bearing&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;Bearing&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="137"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;0&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;0&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="159"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Tilt&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;Tilt&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="176"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;0&quot;, should use `@string` resource"
- errorLine1=" android:text=&quot;0&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="198"
- column="13"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Change intensity&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Change intensity&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_building.xml"
- line="6"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Change Anchor&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Change Anchor&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_building.xml"
- line="10"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Amount of markers&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Amount of markers&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_bulk_marker.xml"
- line="6"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Update layer (invalidate)&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Update layer (invalidate)&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_custom_layer.xml"
- line="7"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Red&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Red&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_custom_layer.xml"
- line="10"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Green&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Green&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_custom_layer.xml"
- line="13"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Blue&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Blue&quot; />"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_custom_layer.xml"
- line="16"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add an exponential zoom function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add an exponential zoom function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="7"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add an interval zoom function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add an interval zoom function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="12"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a categorical source function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a categorical source function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="17"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add an exponential source function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add an exponential source function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="22"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add an identity source function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add an identity source function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="27"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add an interval source function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add an interval source function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="32"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a composite, exponential function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a composite, exponential function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="37"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a composite, categorical function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a composite, categorical function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="42"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a composite, interval function&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a composite, interval function&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_data_driven_style.xml"
- line="47"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;My Location Tracking&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;My Location Tracking&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_padding.xml"
- line="6"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Bangalore&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Bangalore&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_padding.xml"
- line="10"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;List all layers in the style&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;List all layers in the style&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="7"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;List all sources in the style&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;List all sources in the style&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="11"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Color the water&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Color the water&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="15"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Set background opacity&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Set background opacity&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="19"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Set road symbol placement to Point&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Set road symbol placement to Point&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="23"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Set layer visibility to false&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Set layer visibility to false&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="27"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a parks layer&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a parks layer&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="31"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a dynamic GeoJSON source&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a dynamic GeoJSON source&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="35"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Remove buildings layer&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Remove buildings layer&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="39"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a terrain layer&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a terrain layer&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="43"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Add a satellite layer&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Add a satellite layer&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="47"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Change the water color on zoom&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Change the water color on zoom&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="51"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Custom tiles&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Custom tiles&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="55"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Apply filtered fill&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Apply filtered fill&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="59"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Apply filtered line&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Apply filtered line&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="63"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Apply numeric fill filter&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Apply numeric fill filter&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_runtime_style.xml"
- line="67"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Toggle text size&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Toggle text size&quot;/>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_symbol_layer.xml"
- line="6"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Toggle text field contents&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Toggle text field contents&quot;/>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_symbol_layer.xml"
- line="10"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Toggle text font&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Toggle text font&quot;/>"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_symbol_layer.xml"
- line="14"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Zoom in&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Zoom in&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_zoom.xml"
- line="6"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Zoom out&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Zoom out&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_zoom.xml"
- line="10"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Zoom by 2&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Zoom by 2&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_zoom.xml"
- line="14"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Zoom to point&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Zoom to point&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_zoom.xml"
- line="18"
- column="9"/>
- </issue>
-
- <issue
- id="HardcodedText"
- message="[I18N] Hardcoded string &quot;Zoom to 4&quot;, should use `@string` resource"
- errorLine1=" android:title=&quot;Zoom to 4&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/menu/menu_zoom.xml"
- line="22"
- column="9"/>
- </issue>
-
- <issue
- id="RelativeOverlap"
- message="`@id/button_list_regions` can overlap `@id/button_download_region` if @string/button_download_region, @string/button_list_regions grow due to localized text expansion"
- errorLine1=" &lt;Button"
- errorLine2=" ^">
- <location
- file="src/main/res/layout/activity_offline.xml"
- line="33"
- column="5"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_marginEnd=&quot;@dimen/fab_margin&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_marginRight=&quot;@dimen/fab_margin&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_animated_image_source.xml"
- line="25"
- column="5"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_marginEnd=&quot;@dimen/fab_margin&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_marginRight=&quot;@dimen/fab_margin&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_circle_layer.xml"
- line="25"
- column="9"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_marginEnd=&quot;@dimen/fab_margin&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_marginRight=&quot;@dimen/fab_margin&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_circle_layer.xml"
- line="37"
- column="9"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_marginEnd=&quot;@dimen/fab_margin&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_marginRight=&quot;@dimen/fab_margin&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_debug_mode.xml"
- line="32"
- column="9"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_alignParentEnd=&quot;true&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_alignParentRight=&quot;true&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_offline.xml"
- line="38"
- column="9"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_marginEnd=&quot;@dimen/fab_margin&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_marginRight=&quot;@dimen/fab_margin&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_offline.xml"
- line="39"
- column="9"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_marginStart=&quot;56dp&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_marginLeft=&quot;56dp&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_scroll_by.xml"
- line="42"
- column="17"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_marginStart=&quot;56dp&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_marginLeft=&quot;56dp&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/activity_scroll_by.xml"
- line="59"
- column="17"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toStartOf=&quot;@+id/value_lat&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toLeftOf=&quot;@+id/value_lat&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="28"
- column="13"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toEndOf=&quot;@id/text_lat&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toRightOf=&quot;@id/text_lat&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="29"
- column="13"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toStartOf=&quot;@+id/value_lon&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toLeftOf=&quot;@+id/value_lon&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="67"
- column="13"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toEndOf=&quot;@id/text_lon&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toRightOf=&quot;@id/text_lon&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="68"
- column="13"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toStartOf=&quot;@+id/value_zoom&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toLeftOf=&quot;@+id/value_zoom&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="106"
- column="13"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toEndOf=&quot;@id/text_zoom&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toRightOf=&quot;@id/text_zoom&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="107"
- column="13"/>
- </issue>
-
- <issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toStartOf=&quot;@+id/value_bearing&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toLeftOf=&quot;@+id/value_bearing&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-hdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
<location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="145"
- column="13"/>
+ file="src/main/res/drawable-hdpi"/>
</issue>
<issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toEndOf=&quot;@id/text_bearing&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toRightOf=&quot;@id/text_bearing&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-mdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png">
<location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="146"
- column="13"/>
+ file="src/main/res/drawable-mdpi"/>
</issue>
<issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toStartOf=&quot;@+id/value_tilt&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toLeftOf=&quot;@+id/value_tilt&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xhdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
<location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="184"
- column="13"/>
+ file="src/main/res/drawable-xhdpi"/>
</issue>
<issue
- id="RtlHardcoded"
- message="Consider adding `android:layout_toEndOf=&quot;@id/text_tilt&quot;` to better support right-to-left layouts"
- errorLine1=" android:layout_toRightOf=&quot;@id/text_tilt&quot;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xxhdpi`: ic_car_top.png, ic_taxi_top.png, southeast_radar_0.png, southeast_radar_1.png, southeast_radar_2.png... (1 more)">
<location
- file="src/main/res/layout/dialog_camera_position.xml"
- line="185"
- column="13"/>
+ file="src/main/res/drawable-xxhdpi"/>
</issue>
</issues>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java
new file mode 100644
index 0000000000..3e226a7ec5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java
@@ -0,0 +1,42 @@
+package com.mapbox.mapboxsdk.maps;
+
+import com.mapbox.mapboxsdk.annotations.Icon;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+import timber.log.Timber;
+
+public class IconManagerResolver {
+
+ private IconManager iconManager;
+
+ public IconManagerResolver(MapboxMap mapboxMap) {
+ try {
+ Field annotationManagerField = MapboxMap.class.getDeclaredField("annotationManager");
+ annotationManagerField.setAccessible(true);
+ AnnotationManager annotationManager = (AnnotationManager) annotationManagerField.get(mapboxMap);
+
+ Field iconManagerField = AnnotationManager.class.getDeclaredField("iconManager");
+ iconManagerField.setAccessible(true);
+ iconManager = (IconManager) iconManagerField.get(annotationManager);
+ } catch (Exception exception) {
+ Timber.e(exception, "Could not create IconManagerResolver, unable to reflect.");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<Icon, Integer> getIconMap() {
+ try {
+ Field field = IconManager.class.getDeclaredField("iconMap");
+ field.setAccessible(true);
+ return (Map<Icon, Integer>) field.get(iconManager);
+ } catch (NoSuchFieldException exception) {
+ Timber.e(exception, "Could not getIconMap, unable to reflect.");
+ } catch (IllegalAccessException exception) {
+ Timber.e(exception, "Could not getIconMap, unable to reflect.");
+ }
+ return new HashMap<>();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java
new file mode 100644
index 0000000000..33a946d0a1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java
@@ -0,0 +1,146 @@
+package com.mapbox.mapboxsdk.testapp.annotations;
+
+import android.app.Activity;
+import android.support.v4.content.res.ResourcesCompat;
+
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.IconManagerResolver;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * Tests integration between Icons and Markers
+ */
+public class IconTest extends BaseActivityTest {
+
+ private Map<Icon, Integer> iconMap;
+
+ @Before
+ public void beforeTest() {
+ super.beforeTest();
+ iconMap = new IconManagerResolver(getMapboxMap()).getIconMap();
+ }
+
+ @Test
+ public void testEmpty() throws Exception {
+ assertTrue(iconMap.isEmpty());
+ }
+
+ @Test
+ public void testAddSameIconMarker() throws Exception {
+ Icon defaultMarker = IconFactory.getInstance(rule.getActivity()).defaultMarker();
+ getMapboxMap().addMarker(new MarkerOptions().position(new LatLng()));
+ getMapboxMap().addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(1, iconMap.size());
+ assertEquals(2, iconMap.get(defaultMarker), 0);
+ }
+
+ @Test
+ public void testAddDifferentIconMarker() throws Exception {
+ Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.mapbox_logo_icon);
+ getMapboxMap().addMarker(new MarkerOptions().icon(icon).position(new LatLng()));
+ getMapboxMap().addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(iconMap.size(), 2);
+ assertTrue(iconMap.containsKey(icon));
+ assertTrue(iconMap.get(icon) == 1);
+ }
+
+ @Test
+ public void testAddRemoveIconMarker() throws Exception {
+ MapboxMap mapboxMap = getMapboxMap();
+
+ Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.mapbox_logo_icon);
+ Marker marker = mapboxMap.addMarker(new MarkerOptions().icon(icon).position(new LatLng()));
+ mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(iconMap.size(), 2);
+ assertTrue(iconMap.containsKey(icon));
+ assertTrue(iconMap.get(icon) == 1);
+
+ mapboxMap.removeMarker(marker);
+ assertEquals(iconMap.size(), 1);
+ assertFalse(iconMap.containsKey(icon));
+ }
+
+ @Test
+ public void testAddRemoveDefaultMarker() throws Exception {
+ MapboxMap mapboxMap = getMapboxMap();
+
+ Marker marker = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(iconMap.size(), 1);
+
+ mapboxMap.removeMarker(marker);
+ assertEquals(iconMap.size(), 0);
+
+ mapboxMap.addMarker(new MarkerOptions().position(new LatLng()));
+ assertEquals(iconMap.size(), 1);
+ }
+
+ @Test
+ public void testAddRemoveMany() throws Exception {
+ Activity activity = rule.getActivity();
+ MapboxMap mapboxMap = getMapboxMap();
+ IconFactory iconFactory = IconFactory.getInstance(activity);
+
+ // add 2 default icon markers
+ Marker defaultMarkerOne = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ Marker defaultMarkerTwo = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(2, 1)));
+
+ // add 4 unique icon markers
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(iconFactory.fromResource(R.drawable.mapbox_logo_icon))
+ .position(new LatLng(3, 1))
+ );
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(iconFactory.fromResource(R.drawable.mapbox_compass_icon))
+ .position(new LatLng(4, 1))
+ );
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(IconUtils.drawableToIcon(activity, R.drawable.ic_stars,
+ ResourcesCompat.getColor(activity.getResources(),
+ R.color.blueAccent, activity.getTheme())))
+ .position(new LatLng(5, 1))
+ );
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(iconFactory.fromResource(R.drawable.ic_android))
+ .position(new LatLng(6, 1))
+ );
+
+ assertEquals("Amount of icons should match 5", 5, iconMap.size());
+ assertEquals("Refcounter of default marker should match 2", 2, iconMap.get(iconFactory.defaultMarker()), 0);
+
+ mapboxMap.removeMarker(defaultMarkerOne);
+
+ assertEquals("Amount of icons should match 5", 5, iconMap.size());
+ assertEquals("Refcounter of default marker should match 1", 1, iconMap.get(iconFactory.defaultMarker()), 0);
+
+ mapboxMap.removeMarker(defaultMarkerTwo);
+
+ assertEquals("Amount of icons should match 4", 4, iconMap.size());
+ assertNull("DefaultMarker shouldn't exist anymore", iconMap.get(iconFactory.defaultMarker()));
+
+ mapboxMap.clear();
+ assertEquals("Amount of icons should match 0", 0, iconMap.size());
+ }
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java
index fa19235ad0..ec7105c321 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.testapp.maps.widgets;
+import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -97,6 +98,7 @@ public class MyLocationViewTest extends BaseActivityTest {
return getClass().getSimpleName();
}
+ @SuppressLint("MissingPermission")
@Override
public void perform(UiController uiController, View view) {
if (isEnabled) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java
index be2fc9ab9c..5d10cfa38a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java
@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.style;
-import android.content.res.Resources;
import android.support.annotation.RawRes;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
@@ -13,6 +12,7 @@ import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
import com.mapbox.services.commons.geojson.Feature;
import com.mapbox.services.commons.geojson.FeatureCollection;
import com.mapbox.services.commons.geojson.Point;
@@ -21,18 +21,13 @@ import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
+
+import timber.log.Timber;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.junit.Assert.fail;
/**
* Tests for {@link GeoJsonSource}
@@ -46,19 +41,22 @@ public class GeoJsonSourceTests extends BaseActivityTest {
}
@Test
- public void testFeatureCollection() {
+ public void testFeatureCollection() throws Exception {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
- GeoJsonSource source = new GeoJsonSource("source", FeatureCollection
- .fromJson(readRawResource(rule.getActivity().getResources(), R.raw.test_feature_collection)));
+ GeoJsonSource source = null;
+ try {
+ source = new GeoJsonSource("source", FeatureCollection
+ .fromJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_collection)));
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
mapboxMap.addSource(source);
-
mapboxMap.addLayer(new CircleLayer("layer", source.getId()));
}
-
});
}
@@ -79,14 +77,19 @@ public class GeoJsonSourceTests extends BaseActivityTest {
}
@Test
- public void testFeatureProperties() {
+ public void testFeatureProperties() throws IOException {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
- GeoJsonSource source = new GeoJsonSource("source",
- readRawResource(rule.getActivity().getResources(), R.raw.test_feature_properties));
+ GeoJsonSource source = null;
+ try {
+ source = new GeoJsonSource("source",
+ ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties));
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
mapboxMap.addSource(source);
mapboxMap.addLayer(new CircleLayer("layer", source.getId()));
@@ -141,8 +144,11 @@ public class GeoJsonSourceTests extends BaseActivityTest {
Layer layer = new CircleLayer("layer", source.getId());
mapboxMap.addLayer(layer);
- source.setGeoJson(Feature.fromJson(
- readRawResource(rule.getActivity().getResources(), resource)));
+ try {
+ source.setGeoJson(Feature.fromJson(ResourceUtils.readRawResource(rule.getActivity(), resource)));
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
mapboxMap.removeLayer(layer);
mapboxMap.removeSource(source);
@@ -151,27 +157,6 @@ public class GeoJsonSourceTests extends BaseActivityTest {
});
}
- private String readRawResource(Resources resources, @RawRes int rawResource) {
- InputStream is = resources.openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- try {
- Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- int numRead;
- while ((numRead = reader.read(buffer)) != -1) {
- writer.write(buffer, 0, numRead);
- }
- } finally {
- is.close();
- }
- } catch (IOException err) {
- fail(err.getMessage());
- }
-
- return writer.toString();
- }
-
public abstract class BaseViewAction implements ViewAction {
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java
new file mode 100644
index 0000000000..60cf4ced3d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java
@@ -0,0 +1,54 @@
+package com.mapbox.mapboxsdk.testapp.style;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.test.espresso.UiController;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * CRUD tests around Image
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImageTest extends BaseActivityTest {
+
+ private static final String IMAGE_ID = "test.image";
+
+ @Override
+ protected Class getActivityClass() {
+ return RuntimeStyleTestActivity.class;
+ }
+
+ @Test
+ public void testAddGetImage() {
+ validateTestSetup();
+ MapboxMapAction.invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ Drawable drawable = rule.getActivity().getResources().getDrawable(R.drawable.ic_launcher_round);
+ assertTrue(drawable instanceof BitmapDrawable);
+
+ Bitmap bitmapSet = ((BitmapDrawable) drawable).getBitmap();
+ mapboxMap.addImage(IMAGE_ID, bitmapSet);
+
+ Bitmap bitmapGet = mapboxMap.getImage(IMAGE_ID);
+ assertTrue(bitmapGet.sameAs(bitmapSet));
+
+ mapboxMap.removeImage(IMAGE_ID);
+ assertNull(mapboxMap.getImage(IMAGE_ID));
+ }
+ });
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
index e2694af348..f8248ae4a7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
@@ -1214,6 +1214,111 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testIconAnchorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(iconAnchor(ICON_ANCHOR_CENTER));
+ assertEquals((String) layer.getIconAnchor().getValue(), (String) ICON_ANCHOR_CENTER);
+ }
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconAnchor(
+ zoom(
+ interval(
+ stop(2, iconAnchor(ICON_ANCHOR_CENTER))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getIconAnchor());
+ assertNotNull(layer.getIconAnchor().getFunction());
+ assertEquals(CameraFunction.class, layer.getIconAnchor().getFunction().getClass());
+ assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
+ assertEquals(1, ((IntervalStops) layer.getIconAnchor().getFunction().getStops()).size());
+ }
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconAnchor(property("FeaturePropertyA", Stops.<String>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getIconAnchor());
+ assertNotNull(layer.getIconAnchor().getFunction());
+ assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconAnchor(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, iconAnchor(ICON_ANCHOR_CENTER))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getIconAnchor());
+ assertNotNull(layer.getIconAnchor().getFunction());
+ assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty());
+ assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
public void testIconPitchAlignmentAsConstant() {
validateTestSetup();
setupLayer();
@@ -1742,6 +1847,139 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextMaxWidthAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextMaxWidthAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, textMaxWidth(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextMaxWidthAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textMaxWidth(0.3f))
+ )
+ ).withDefaultValue(textMaxWidth(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue());
+ }
+ });
+
+ }
+
+ @Test
+ public void testTextMaxWidthAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textMaxWidth(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, textMaxWidth(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(textMaxWidth(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextMaxWidth());
+ assertNotNull(layer.getTextMaxWidth().getFunction());
+ assertEquals(CompositeFunction.class, layer.getTextMaxWidth().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextMaxWidth().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getTextMaxWidth().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextMaxWidth().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+ });
+ }
+
+ @Test
public void testTextLineHeightAsConstant() {
validateTestSetup();
setupLayer();
@@ -1840,6 +2078,139 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextLetterSpacingAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextLetterSpacingAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, textLetterSpacing(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextLetterSpacingAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textLetterSpacing(0.3f))
+ )
+ ).withDefaultValue(textLetterSpacing(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue());
+ }
+ });
+
+ }
+
+ @Test
+ public void testTextLetterSpacingAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textLetterSpacing(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, textLetterSpacing(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(textLetterSpacing(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextLetterSpacing());
+ assertNotNull(layer.getTextLetterSpacing().getFunction());
+ assertEquals(CompositeFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getTextLetterSpacing().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextLetterSpacing().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+ });
+ }
+
+ @Test
public void testTextJustifyAsConstant() {
validateTestSetup();
setupLayer();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java
index 6e582c6a3a..1c4981ca5e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java
@@ -3,20 +3,28 @@ package com.mapbox.mapboxsdk.testapp.utils;
import android.app.Activity;
import android.support.test.espresso.IdlingResource;
-import timber.log.Timber;
-
+import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import java.lang.reflect.Field;
-public class OnMapReadyIdlingResource implements IdlingResource {
+public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallback {
- private final Activity activity;
private MapboxMap mapboxMap;
private IdlingResource.ResourceCallback resourceCallback;
+ private Activity activity;
public OnMapReadyIdlingResource(Activity activity) {
this.activity = activity;
+ try {
+ Field field = activity.getClass().getDeclaredField("mapView");
+ field.setAccessible(true);
+ ((MapView) field.get(activity)).getMapAsync(this);
+ } catch (Exception err) {
+ throw new RuntimeException(err);
+ }
+
}
@Override
@@ -26,11 +34,7 @@ public class OnMapReadyIdlingResource implements IdlingResource {
@Override
public boolean isIdleNow() {
- boolean idle = isMapboxMapReady();
- if (idle && resourceCallback != null) {
- resourceCallback.onTransitionToIdle();
- }
- return idle;
+ return mapboxMap != null;
}
@Override
@@ -38,20 +42,15 @@ public class OnMapReadyIdlingResource implements IdlingResource {
this.resourceCallback = resourceCallback;
}
- private boolean isMapboxMapReady() {
- try {
- Field field = activity.getClass().getDeclaredField("mapboxMap");
- field.setAccessible(true);
- mapboxMap = (MapboxMap) field.get(activity);
- Timber.e("isMapboxReady called with value " + (mapboxMap != null));
- return mapboxMap != null;
- } catch (Exception exception) {
- Timber.e("could not reflect", exception);
- return false;
- }
- }
-
public MapboxMap getMapboxMap() {
return mapboxMap;
}
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ if (resourceCallback != null) {
+ resourceCallback.onTransitionToIdle();
+ }
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 9ef85be755..bf97749b9e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -113,6 +113,17 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
+ android:name=".activity.camera.CameraAnimatorActivity"
+ android:description="@string/description_camera_animator"
+ android:label="@string/activity_camera_animator">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
android:name=".activity.camera.CameraPositionActivity"
android:description="@string/description_cameraposition"
android:label="@string/activity_camera_position">
@@ -305,14 +316,12 @@
</activity>
<activity
android:name=".activity.maplayout.DebugModeActivity"
+ android:configChanges="orientation|keyboardHidden|screenSize"
android:description="@string/description_debug_mode"
android:label="@string/activity_debug_mode">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value="@string/category_basic"/>
</activity>
<activity
android:name=".activity.offline.OfflineActivity"
@@ -358,6 +367,16 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
+ <activity android:name=".activity.snapshot.MapSnapshotterActivity"
+ android:description="@string/description_map_snapshotter"
+ android:label="@string/activity_map_snapshotter">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_imagegenerator"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
<activity
android:name=".activity.maplayout.DoubleMapActivity"
android:description="@string/description_doublemap"
@@ -392,18 +411,6 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
- android:name=".activity.maplayout.NavigationDrawerActivity"
- android:description="@string/description_navigation_drawer"
- android:label="@string/activity_navigation_drawer"
- android:theme="@style/AppTheme.ActionBar.Transparent">
- <meta-data
- android:name="@string/category"
- android:value="@string/category_maplayout"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
- </activity>
- <activity
android:name=".activity.maplayout.SimpleMapActivity"
android:description="@string/description_simple_map"
android:label="@string/activity_simple_map">
@@ -626,12 +633,23 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
- android:name=".activity.annotation.AddRemoveMarkerActivity"
+ android:name=".activity.style.SymbolGeneratorActivity"
+ android:description="@string/description_symbol_generator"
+ android:label="@string/activity_symbol_generator">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_style"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
+ android:name=".activity.style.ZoomFunctionSymbolLayerActivity"
android:description="@string/description_add_remove_markers"
android:label="@string/activity_add_remove_markers">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_style"/>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
@@ -687,7 +705,7 @@
android:label="@string/activity_bottom_sheet">
<meta-data
android:name="@string/category"
- android:value="@string/category_basic"/>
+ android:value="@string/category_maplayout"/>
</activity>
<!-- For Instrumentation tests -->
@@ -717,4 +735,4 @@
<!-- android:value="true" /> -->
</application>
-</manifest>
+</manifest> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
index deee312bb3..fba33bb380 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp;
import android.app.Application;
import android.os.StrictMode;
import android.text.TextUtils;
-import android.util.Log;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.testapp.utils.TokenUtils;
@@ -21,7 +20,6 @@ import static timber.log.Timber.DebugTree;
*/
public class MapboxApplication extends Application {
- private static final String LOG_TAG = MapboxApplication.class.getSimpleName();
private static final String DEFAULT_MAPBOX_ACCESS_TOKEN = "YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE";
private static final String ACCESS_TOKEN_NOT_SET_MESSAGE = "In order to run the Test App you need to set a valid "
+ "access token. During development, you can set the MAPBOX_ACCESS_TOKEN environment variable for the SDK to "
@@ -55,7 +53,7 @@ public class MapboxApplication extends Application {
String mapboxAccessToken = TokenUtils.getMapboxAccessToken(getApplicationContext());
if (TextUtils.isEmpty(mapboxAccessToken) || mapboxAccessToken.equals(DEFAULT_MAPBOX_ACCESS_TOKEN)) {
- Log.w(LOG_TAG, ACCESS_TOKEN_NOT_SET_MESSAGE);
+ Timber.e(ACCESS_TOKEN_NOT_SET_MESSAGE);
}
Mapbox.getInstance(getApplicationContext(), mapboxAccessToken);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
index f8617366a0..3f20f19f5d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
@@ -92,7 +92,7 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis
getPackageManager().getPackageInfo(getPackageName(),
PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException exception) {
- Timber.e("Could not resolve package info", exception);
+ Timber.e(exception, "Could not resolve package info");
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java
deleted file mode 100644
index 27958c3d0c..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java
+++ /dev/null
@@ -1,188 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.annotation;
-
-import android.annotation.SuppressLint;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.annotation.DrawableRes;
-import android.support.v7.app.AppCompatActivity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.mapbox.mapboxsdk.annotations.Icon;
-import com.mapbox.mapboxsdk.annotations.IconFactory;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.utils.ViewToBitmapUtil;
-
-import timber.log.Timber;
-
-/**
- * Test activity showcasing updating a Marker image when changing zoom levels
- */
-public class AddRemoveMarkerActivity extends AppCompatActivity {
-
- public static final double THRESHOLD = 5.0;
-
- private MapView mapView;
- private MapboxMap mapboxMap;
- private double lastZoom;
- private boolean isShowingHighThresholdMarker;
- private boolean isShowingLowThresholdMarker;
-
- private MarkerOptions lowThresholdMarker;
- private MarkerOptions highThresholdMarker;
- private Marker activeMarker;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_add_remove_marker);
-
- View lowThresholdView = generateView("Low", R.drawable.ic_circle);
- Bitmap lowThresholdBitmap = ViewToBitmapUtil.convertToBitmap(lowThresholdView);
- Icon lowThresholdIcon = IconFactory.getInstance(this).fromBitmap(lowThresholdBitmap);
-
- lowThresholdMarker = new MarkerOptions()
- .icon(lowThresholdIcon)
- .position(new LatLng(0.1, 0));
-
- View highThesholdView = generateView("High", R.drawable.ic_circle);
- Bitmap highThresholdBitmap = ViewToBitmapUtil.convertToBitmap(highThesholdView);
- Icon highThresholdIcon = IconFactory.getInstance(this).fromBitmap(highThresholdBitmap);
-
- highThresholdMarker = new MarkerOptions()
- .icon(highThresholdIcon)
- .position(new LatLng(0.1, 0));
-
- mapView = (MapView) findViewById(R.id.mapView);
- mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- AddRemoveMarkerActivity.this.mapboxMap = mapboxMap;
- updateZoom(mapboxMap.getCameraPosition().zoom);
- mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4.9f));
- mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- updateZoom(position.zoom);
- }
- });
- }
- });
- }
-
- @SuppressLint("InflateParams")
- private View generateView(String text, @DrawableRes int drawableRes) {
- View view = LayoutInflater.from(this).inflate(R.layout.view_custom_marker, null);
- TextView textView = (TextView) view.findViewById(R.id.textView);
- textView.setText(text);
- textView.setTextColor(Color.WHITE);
- ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
- imageView.setImageResource(drawableRes);
- return view;
- }
-
- private void updateZoom(double zoom) {
- if (lastZoom == zoom) {
- return;
- }
-
- lastZoom = zoom;
- if (zoom > THRESHOLD) {
- showHighThresholdMarker();
- } else {
- showLowThresholdMarker();
- }
- }
-
- private void showLowThresholdMarker() {
- if (isShowingLowThresholdMarker) {
- return;
- }
-
- isShowingLowThresholdMarker = true;
- isShowingHighThresholdMarker = false;
-
- if (activeMarker != null) {
- Timber.d("Remove marker with " + activeMarker.getId());
- mapboxMap.removeMarker(activeMarker);
- } else {
- Timber.e("active marker is null");
- }
-
- activeMarker = mapboxMap.addMarker(lowThresholdMarker);
- Timber.d("showLowThresholdMarker() " + activeMarker.getId());
- }
-
- private void showHighThresholdMarker() {
- if (isShowingHighThresholdMarker) {
- return;
- }
-
- isShowingLowThresholdMarker = false;
- isShowingHighThresholdMarker = true;
-
- if (activeMarker != null) {
- Timber.d("Remove marker with " + activeMarker.getId());
- mapboxMap.removeMarker(activeMarker);
- } else {
- Timber.e("active marker is null");
- }
-
- activeMarker = mapboxMap.addMarker(highThresholdMarker);
- Timber.d("showHighThresholdMarker() " + activeMarker.getId());
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- mapView.onStart();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mapView.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mapView.onPause();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- mapView.onStop();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
index 8b238e49a8..50adeb2d74 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
@@ -274,7 +274,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
String json = GeoParseUtil.loadStringFromAssets(activity.getApplicationContext(), "points.geojson");
return GeoParseUtil.parseGeoJsonCoordinates(json);
} catch (IOException | JSONException exception) {
- Timber.e("Could not add markers,", exception);
+ Timber.e(exception, "Could not add markers");
return null;
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
index f2f82865d1..61ece0a94f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
@@ -36,6 +36,7 @@ import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerViewOptions;
import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerView;
import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerViewOptions;
+import java.util.Locale;
import java.util.Random;
/**
@@ -150,7 +151,11 @@ public class MarkerViewActivity extends AppCompatActivity {
public void onMapChanged(@MapView.MapChange int change) {
if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) {
if (!markerViewManager.getMarkerViewAdapters().isEmpty() && viewCountView != null) {
- viewCountView.setText("ViewCache size " + markerViewContainer.getChildCount());
+ viewCountView.setText(String.format(
+ Locale.getDefault(),
+ getString(R.string.viewcache_size),
+ markerViewContainer.getChildCount())
+ );
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java
index 266db3fdd1..848eab9a3c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java
@@ -19,6 +19,9 @@ import java.util.List;
import timber.log.Timber;
+/**
+ * Test activity showcasing counting MarkerViews in a rectangle.
+ */
public class MarkerViewsInRectangleActivity extends AppCompatActivity implements OnMapReadyCallback,
View.OnClickListener {
@@ -53,7 +56,7 @@ public class MarkerViewsInRectangleActivity extends AppCompatActivity implements
int top = selectionBox.getTop() - mapView.getTop();
int left = selectionBox.getLeft() - mapView.getLeft();
RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
- Timber.i(String.format("Querying box %s", box));
+ Timber.i("Querying box %s", box);
List<MarkerView> markers = mapboxMap.getMarkerViewsInRect(box);
// Show count
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
index fecfe2a842..49c9663672 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
@@ -57,8 +57,6 @@ public class PolygonActivity extends AppCompatActivity implements OnMapReadyCall
// configure inital map state
MapboxMapOptions options = new MapboxMapOptions()
.attributionTintColor(RED_COLOR)
- // deprecated feature!
- .textureMode(true)
.compassFadesWhenFacingNorth(false)
.styleUrl(Style.MAPBOX_STREETS)
.camera(new CameraPosition.Builder()
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
new file mode 100644
index 0000000000..cc44ac9715
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
@@ -0,0 +1,174 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.os.Bundle;
+import android.support.v4.view.animation.FastOutLinearInInterpolator;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.animation.AnticipateOvershootInterpolator;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+/**
+ * Test activity showcasing using Android SDK animators to animate camera position changes.
+ */
+public class CameraAnimatorActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private static final double ANIMATION_DELAY_FACTOR = 1.5;
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_camera_animator);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ if (mapView != null) {
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+ }
+
+ @Override
+ public void onMapReady(final MapboxMap map) {
+ mapboxMap = map;
+ findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ view.setVisibility(View.GONE);
+ createAnimator(mapboxMap.getCameraPosition()).start();
+ }
+ });
+ }
+
+ private Animator createAnimator(CameraPosition currentPosition) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.play(createLatLngAnimator(currentPosition.target));
+ animatorSet.play(createZoomAnimator(currentPosition.zoom));
+ animatorSet.play(createBearingAnimator(currentPosition.bearing));
+ animatorSet.play(createTiltAnimator(currentPosition.tilt));
+ return animatorSet;
+ }
+
+ private Animator createLatLngAnimator(LatLng currentPosition) {
+ LatLng target = new LatLng(37.789992, -122.402214);
+ ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, target);
+ latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ latLngAnimator.setInterpolator(new FastOutSlowInInterpolator());
+ latLngAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setLatLng((LatLng) animation.getAnimatedValue());
+ }
+ });
+ return latLngAnimator;
+ }
+
+ private Animator createZoomAnimator(double currentZoom) {
+ ValueAnimator zoomAnimator = ValueAnimator.ofFloat((float) currentZoom, 14.5f);
+ zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR));
+ zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR));
+ zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator());
+ zoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setZoom((Float) animation.getAnimatedValue());
+ }
+ });
+ return zoomAnimator;
+ }
+
+ private Animator createBearingAnimator(double currentBearing) {
+ ValueAnimator bearingAnimator = ValueAnimator.ofFloat((float) currentBearing, 135);
+ bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR));
+ bearingAnimator.setInterpolator(new FastOutLinearInInterpolator());
+ bearingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setBearing((Float) animation.getAnimatedValue());
+ }
+ });
+ return bearingAnimator;
+ }
+
+ private Animator createTiltAnimator(double currentTilt) {
+ ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, 60);
+ tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR));
+ tiltAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mapboxMap.setTilt((Float) animation.getAnimatedValue());
+ }
+ });
+ return tiltAnimator;
+ }
+
+ private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
+
+ private final LatLng latLng = new LatLng();
+
+ @Override
+ public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
+ latLng.setLatitude(startValue.getLatitude()
+ + ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
+ latLng.setLongitude(startValue.getLongitude()
+ + ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
+ return latLng;
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
index cb2f57d860..2820bdbd53 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.camera;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
@@ -24,6 +25,9 @@ import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
+/**
+ * Test activity showcasing how to listen to camera change events.
+ */
public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback {
private MapView mapView;
@@ -67,7 +71,7 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
}
});
- mapboxMap.setOnCameraMoveStartedistener(new MapboxMap.OnCameraMoveStartedListener() {
+ mapboxMap.setOnCameraMoveStartedListener(new MapboxMap.OnCameraMoveStartedListener() {
private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"};
@@ -83,6 +87,7 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary));
fab.setOnClickListener(new View.OnClickListener() {
+ @SuppressLint("InflateParams")
@Override
public void onClick(View view) {
Context context = view.getContext();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
index d81538f323..e3a9551b8e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
@@ -1,75 +1,122 @@
package com.mapbox.mapboxsdk.testapp.activity.camera;
import android.os.Bundle;
-import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.design.widget.BottomSheetBehavior;
import android.support.v7.app.AppCompatActivity;
+import android.view.View;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.view.LockableBottomSheetBehavior;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Test activity showcasing using the LatLngBounds camera API.
- * <p>
- * This activity opens the map at zoom level 0 and animates into a bounds set by Los Angeles and New York
- * with some additional padding and an animation duration of 1500 ms.
- * </p>
*/
-public class LatLngBoundsActivity extends AppCompatActivity implements OnMapReadyCallback {
-
- private static final LatLng LOS_ANGELES = new LatLng(34.053940, -118.242622);
- private static final LatLng NEW_YORK = new LatLng(40.712730, -74.005953);
-
- private final LatLng CHINA_BOTTOM_LEFT = new LatLng(15.68169, 73.499857);
- private final LatLng CHINA_TOP_RIGHT = new LatLng(53.560711, 134.77281);
+public class LatLngBoundsActivity extends AppCompatActivity implements View.OnClickListener {
+
+ private static final List<LatLng> LOCATIONS = new ArrayList<LatLng>() {
+ {
+ add(new LatLng(37.806866, -122.422502));
+ add(new LatLng(37.812905, -122.477605));
+ add(new LatLng(37.826944, -122.423188));
+ add(new LatLng(37.752676, -122.447736));
+ add(new LatLng(37.769305, -122.479322));
+ add(new LatLng(37.749834, -122.417867));
+ add(new LatLng(37.756149, -122.405679));
+ add(new LatLng(37.751403, -122.387397));
+ add(new LatLng(37.793064, -122.391517));
+ add(new LatLng(37.769122, -122.427394));
+ }
+ };
+ private static final LatLngBounds BOUNDS = new LatLngBounds.Builder().includes(LOCATIONS).build();
+ private static final int ANIMATION_DURATION_LONG = 450;
+ private static final int ANIMATION_DURATION_SHORT = 250;
+ private static final int BOUNDS_PADDING_DIVIDER_SMALL = 3;
+ private static final int BOUNDS_PADDING_DIVIDER_LARGE = 9;
private MapView mapView;
private MapboxMap mapboxMap;
+ private View bottomSheet;
+ private LockableBottomSheetBehavior bottomSheetBehavior;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_visible_bounds);
-
+ setContentView(R.layout.activity_latlngbounds);
mapView = (MapView) findViewById(R.id.mapView);
- mapView.setStyleUrl(Style.DARK);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(this);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(final MapboxMap map) {
+ mapboxMap = map;
+ initMap();
+ }
+ });
+ }
+
+ private void initMap() {
+ addMarkers();
+ initFab();
+ initBottomSheet();
+ moveToBounds(bottomSheet.getMeasuredHeight(), BOUNDS_PADDING_DIVIDER_SMALL, 0);
+ }
+
+ private void addMarkers() {
+ for (LatLng location : LOCATIONS) {
+ mapboxMap.addMarker(new MarkerOptions().position(location));
+ }
+ }
+
+ private void initFab() {
+ findViewById(R.id.fab).setOnClickListener(this);
}
@Override
- public void onMapReady(final MapboxMap map) {
- mapboxMap = map;
- moveToBounds(new LatLngBounds.Builder().include(NEW_YORK).include(LOS_ANGELES).build(), new int[] {0, 0, 0, 0});
- new Handler().postDelayed(new Runnable() {
+ public void onClick(View v) {
+ bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
+ v.animate().alpha(0.0f).setDuration(ANIMATION_DURATION_SHORT);
+ }
+
+ private void initBottomSheet() {
+ bottomSheet = findViewById(R.id.bottom_sheet);
+ bottomSheetBehavior = (LockableBottomSheetBehavior) BottomSheetBehavior.from(bottomSheet);
+ bottomSheetBehavior.setLocked(true);
+ bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
+ @Override
+ public void onStateChanged(@NonNull View bottomSheet, int newState) {
+ if (newState == BottomSheetBehavior.STATE_SETTLING) {
+ moveToBounds(0, BOUNDS_PADDING_DIVIDER_LARGE, ANIMATION_DURATION_LONG);
+ }
+ }
+
@Override
- public void run() {
- moveToBounds(new LatLngBounds.Builder().include(CHINA_BOTTOM_LEFT).include(CHINA_TOP_RIGHT).build(),
- new int[] {100, 100, 100, 100 });
+ public void onSlide(@NonNull View bottomSheet, float slideOffset) {
+
}
- }, 5000);
+ });
}
- private void moveToBounds(LatLngBounds latLngBounds, int[] padding) {
- mapboxMap.clear();
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getNorthEast()));
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getSouthEast()));
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getSouthWest()));
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getNorthWest()));
- CameraUpdate update =
- CameraUpdateFactory.newLatLngBounds(latLngBounds,
- padding[0],
- padding[1],
- padding[2],
- padding[3]);
- mapboxMap.moveCamera(update);
+ private void moveToBounds(int verticalOffset, int boundsPaddingDivider, int duration) {
+ int paddingHorizontal = mapView.getMeasuredWidth() / boundsPaddingDivider;
+ int paddingVertical = (mapView.getMeasuredHeight() - verticalOffset) / boundsPaddingDivider;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(
+ BOUNDS,
+ paddingHorizontal,
+ paddingVertical,
+ paddingHorizontal,
+ paddingVertical + verticalOffset),
+ duration
+ );
}
@Override
@@ -97,9 +144,9 @@ public class LatLngBoundsActivity extends AppCompatActivity implements OnMapRead
}
@Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
}
@Override
@@ -109,8 +156,8 @@ public class LatLngBoundsActivity extends AppCompatActivity implements OnMapRead
}
@Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
index 014743df96..53a5800f26 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
@@ -13,6 +13,9 @@ import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
+/**
+ * Test activity showcasing using maximum and minimum zoom levels to restrict camera movement.
+ */
public class MaxMinZoomActivity extends AppCompatActivity implements OnMapReadyCallback {
private MapView mapView;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
index dde22db2db..1b49e9e3d6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
@@ -69,6 +69,7 @@ public class CustomLayerActivity extends AppCompatActivity {
ExampleCustomLayer.createContext(),
ExampleCustomLayer.InitializeFunction,
ExampleCustomLayer.RenderFunction,
+ ExampleCustomLayer.ContextLostFunction, // Optional
ExampleCustomLayer.DeinitializeFunction);
mapboxMap.addLayerBelow(customLayer, "building");
fab.setImageResource(R.drawable.ic_layers_clear);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
index 9c031ad32a..20174daeaa 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
@@ -48,7 +48,7 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity {
int top = selectionBox.getTop() - mapView.getTop();
int left = selectionBox.getLeft() - mapView.getLeft();
RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
- Timber.i(String.format("Querying box %s", box));
+ Timber.i("Querying box %s", box);
List<Feature> features = mapboxMap.queryRenderedFeatures(box);
// Show count
@@ -66,22 +66,21 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity {
}
private void debugOutput(List<Feature> features) {
- Timber.i(String.format("Got %s features", features.size()));
+ Timber.i("Got %s features", features.size());
for (Feature feature : features) {
if (feature != null) {
- Timber.i(String.format("Got feature %s with %s properties and Geometry %s",
+ Timber.i("Got feature %s with %s properties and Geometry %s",
feature.getId(),
feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>",
- feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>")
+ feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>"
);
if (feature.getProperties() != null) {
for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
- Timber.i(String.format("Prop %s - %s", entry.getKey(), entry.getValue()));
+ Timber.i("Prop %s - %s", entry.getKey(), entry.getValue());
}
}
} else {
- // TODO Question: Why not formatting here??
- Timber.i("Got NULL feature %s");
+ Timber.i("Got 0 features");
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
index 1d15efef84..480e48437a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
@@ -46,6 +46,15 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
@Override
public void onMapReady(final MapboxMap mapboxMap) {
QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap;
+
+ // Add layer / source
+ final GeoJsonSource source = new GeoJsonSource("highlighted-shapes-source");
+ mapboxMap.addSource(source);
+ mapboxMap.addLayer(
+ new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source")
+ .withProperties(fillColor(Color.RED))
+ );
+
selectionBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -53,7 +62,7 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
int top = selectionBox.getTop() - mapView.getTop();
int left = selectionBox.getLeft() - mapView.getLeft();
RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
- Timber.i(String.format("Querying box %s for buildings", box));
+ Timber.i("Querying box %s for buildings", box);
List<Feature> features = mapboxMap.queryRenderedFeatures(box, Filter.lt("height", 10), "building");
// Show count
@@ -62,17 +71,8 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
String.format("%s features in box", features.size()),
Toast.LENGTH_SHORT).show();
- // remove layer / source if already added
- mapboxMap.removeSource("highlighted-shapes-source");
- mapboxMap.removeLayer("highlighted-shapes-layer");
-
- // Add layer / source
- mapboxMap.addSource(
- new GeoJsonSource("highlighted-shapes-source",
- FeatureCollection.fromFeatures(features))
- );
- mapboxMap.addLayer(new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source")
- .withProperties(fillColor(Color.RED)));
+ // Update source data
+ source.setGeoJson(FeatureCollection.fromFeatures(features));
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java
index 8c2f3a8fb5..220137d07d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.feature;
import android.graphics.BitmapFactory;
import android.graphics.RectF;
import android.os.Bundle;
-import android.support.annotation.RawRes;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
@@ -14,15 +13,10 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
import com.mapbox.services.commons.geojson.Feature;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
import java.util.List;
import timber.log.Timber;
@@ -57,9 +51,10 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi
// Add a symbol layer (also works with annotations)
try {
- mapboxMap.addSource(new GeoJsonSource("symbols-source", readRawResource(R.raw.test_points_utrecht)));
+ mapboxMap.addSource(new GeoJsonSource("symbols-source", ResourceUtils.readRawResource(
+ QueryRenderedFeaturesBoxSymbolCountActivity.this, R.raw.test_points_utrecht)));
} catch (IOException ioException) {
- Timber.e("Could not load geojson: " + ioException.getMessage());
+ Timber.e(ioException, "Could not load geojson");
return;
}
mapboxMap.addImage(
@@ -76,7 +71,7 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi
int top = selectionBox.getTop() - mapView.getTop();
int left = selectionBox.getLeft() - mapView.getLeft();
RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
- Timber.i(String.format("Querying box %s", box));
+ Timber.i("Querying box %s", box);
List<Feature> features = mapboxMap.queryRenderedFeatures(box, "symbols-layer");
// Show count
@@ -94,23 +89,6 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi
});
}
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- int numRead;
- while ((numRead = reader.read(buffer)) != -1) {
- writer.write(buffer, 0, numRead);
- }
- } finally {
- is.close();
- }
-
- return writer.toString();
- }
-
public MapboxMap getMapboxMap() {
return mapboxMap;
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
index 8b83db3069..1bd6a34b7c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
@@ -59,9 +59,9 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
public void onMapClick(@NonNull LatLng point) {
// Query
final PointF pixel = mapboxMap.getProjection().toScreenLocation(point);
- Timber.i(String.format(
+ Timber.i(
"Requesting features for %sx%s (%sx%s adjusted for density)",
- pixel.x, pixel.y, pixel.x / density, pixel.y / density)
+ pixel.x, pixel.y, pixel.x / density, pixel.y / density
);
List<Feature> features = mapboxMap.queryRenderedFeatures(pixel);
@@ -84,22 +84,21 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
}
private void debugOutput(List<Feature> features) {
- Timber.i(String.format("Got %s features", features.size()));
+ Timber.i("Got %s features", features.size());
for (Feature feature : features) {
if (feature != null) {
- Timber.i(String.format("Got feature %s with %s properties and Geometry %s",
+ Timber.i("Got feature %s with %s properties and Geometry %s",
feature.getId(),
feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>",
- feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>")
+ feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>"
);
if (feature.getProperties() != null) {
for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
- Timber.i(String.format("Prop %s - %s", entry.getKey(), entry.getValue()));
+ Timber.i("Prop %s - %s", entry.getKey(), entry.getValue());
}
}
} else {
- // TODO Question: Why not formatting here??
- Timber.i("Got NULL feature %s");
+ Timber.i("Got NULL feature");
}
}
}
@@ -185,7 +184,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
private final List<Feature> features;
- public CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) {
+ CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) {
super(baseMarkerOptions);
this.features = features;
}
@@ -201,7 +200,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
return this;
}
- public CustomMarkerOptions() {
+ CustomMarkerOptions() {
}
private CustomMarkerOptions(Parcel in) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
index 2be47b4e25..655012f799 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
@@ -15,6 +15,8 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+import java.util.Locale;
+
/**
* Test activity showcasing the Snapshot API to create and display a bitmap of the current shown Map.
*/
@@ -56,7 +58,7 @@ public class SnapshotActivity extends AppCompatActivity implements OnMapReadyCal
snapshotView.setImageBitmap(snapshot);
Toast.makeText(
SnapshotActivity.this,
- String.format("Snapshot taken in %d ms", duration),
+ String.format(Locale.getDefault(), "Snapshot taken in %d ms", duration),
Toast.LENGTH_LONG).show();
}
});
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java
index 18db2ba8a8..7fa4792a38 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.infowindow;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
@@ -21,16 +20,18 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
+import java.util.Locale;
+
/**
* Test activity showcasing how to dynamically update InfoWindow when Using an MapboxMap.InfoWindowAdapter.
*/
public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implements OnMapReadyCallback {
+ private static final LatLng PARIS = new LatLng(48.864716, 2.349014);
+
private MapboxMap mapboxMap;
private MapView mapView;
- private LatLng paris = new LatLng(48.864716, 2.349014);
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -43,7 +44,6 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
@Override
public void onMapReady(MapboxMap map) {
-
mapboxMap = map;
// Add info window adapter
@@ -64,27 +64,32 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
double distanceKm = marker.getPosition().distanceTo(point) / 1000;
// Get the info window
- InfoWindow infoWindow = marker.getInfoWindow();
+ final InfoWindow infoWindow = marker.getInfoWindow();
// Get the view from the info window
if (infoWindow != null && infoWindow.getView() != null) {
// Set the new text on the text view in the info window
- ((TextView) infoWindow.getView()).setText(String.format("%.2fkm", distanceKm));
-
- // Update the info window position (as the text length changes)
- infoWindow.update();
+ TextView textView = (TextView) infoWindow.getView();
+ textView.setText(String.format(Locale.getDefault(), "%.2fkm", distanceKm));
+ textView.post(new Runnable() {
+ @Override
+ public void run() {
+ // Update the info window position (as the text length changes)
+ infoWindow.update();
+ }
+ });
}
}
});
// Focus on Paris
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(paris));
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(PARIS));
}
private MarkerView addMarker(MapboxMap mapboxMap) {
return mapboxMap.addMarker(
new MarkerViewOptions()
- .position(paris)
+ .position(PARIS)
.icon(IconUtils.drawableToIcon(this, R.drawable.ic_location_city,
ResourcesCompat.getColor(getResources(), R.color.mapbox_blue, getTheme()))
));
@@ -94,15 +99,13 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
final int padding = (int) getResources().getDimension(R.dimen.attr_margin);
mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
- @Nullable
@Override
public View getInfoWindow(@NonNull Marker marker) {
TextView textView = new TextView(DynamicInfoWindowAdapterActivity.this);
textView.setText(marker.getTitle());
textView.setBackgroundColor(Color.WHITE);
- textView.setText("Click the map to calculate the distance");
+ textView.setText(R.string.action_calculate_distance);
textView.setPadding(padding, padding, padding, padding);
-
return textView;
}
});
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
index 7026a797d5..22347f8a92 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
@@ -18,6 +18,9 @@ import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarker;
import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarkerOptions;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
+/**
+ * Test activity showcasing using an InfoWindowAdapter to provide a custom InfoWindow content.
+ */
public class InfoWindowAdapterActivity extends AppCompatActivity {
private MapView mapView;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
index 1f016e430b..2c83f6d908 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
@@ -26,6 +26,9 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
+/**
+ * Test activity showcasing using a bottomView with a MapView and stacking map fragments below.
+ */
public class BottomSheetActivity extends AppCompatActivity {
private static final String TAG_MAIN_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.main";
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
index 18d6fadcb8..6134b4cb7a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
@@ -1,10 +1,19 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+import android.content.Context;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
import android.widget.TextView;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@@ -12,18 +21,25 @@ import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.testapp.R;
+import java.util.List;
+import java.util.Locale;
+
import timber.log.Timber;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+
/**
- * Test Activity showcasing the different debug modes and allows to cycle between the default map styles.
+ * Test activity showcasing the different debug modes and allows to cycle between the default map styles.
*/
-public class DebugModeActivity extends AppCompatActivity {
+public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCallback {
private MapView mapView;
private MapboxMap mapboxMap;
-
+ private ActionBarDrawerToggle actionBarDrawerToggle;
private int currentStyleIndex = 0;
private static final String[] STYLES = new String[] {
@@ -32,49 +48,128 @@ public class DebugModeActivity extends AppCompatActivity {
Style.LIGHT,
Style.DARK,
Style.SATELLITE,
- Style.SATELLITE_STREETS
+ Style.SATELLITE_STREETS,
+ Style.TRAFFIC_DAY,
+ Style.TRAFFIC_NIGHT
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_debug_mode);
+ setupToolbar();
+ setupMapView(savedInstanceState);
+ setupDebugChangeView();
+ setupStyleChangeView();
+ }
+
+ private void setupToolbar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ actionBarDrawerToggle = new ActionBarDrawerToggle(this,
+ drawerLayout,
+ R.string.navigation_drawer_open,
+ R.string.navigation_drawer_close
+ );
+ actionBarDrawerToggle.setDrawerIndicatorEnabled(true);
+ actionBarDrawerToggle.syncState();
+ }
+ }
+ private void setupMapView(Bundle savedInstanceState) {
mapView = (MapView) findViewById(R.id.mapView);
+ mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
+ @Override
+ public void onMapChanged(int change) {
+ if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) {
+ Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson());
+ setupNavigationView(mapboxMap.getLayers());
+ }
+ }
+ });
+
mapView.setTag(true);
mapView.setStyleUrl(STYLES[currentStyleIndex]);
mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap map) {
- mapboxMap = map;
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ mapboxMap.getUiSettings().setZoomControlsEnabled(true);
- mapboxMap.getUiSettings().setZoomControlsEnabled(true);
+ setupNavigationView(mapboxMap.getLayers());
+ setupZoomView();
+ setFpsView();
+ }
- // show current zoom level on screen
- final TextView textView = (TextView) findViewById(R.id.textZoom);
- mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- textView.setText(String.format(getString(R.string.debug_zoom), position.zoom));
- }
- });
+ private void setFpsView() {
+ final TextView fpsView = (TextView) findViewById(R.id.fpsView);
+ mapboxMap.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() {
+ @Override
+ public void onFpsChanged(double fps) {
+ fpsView.setText(String.format(Locale.US,"FPS: %4.2f", fps));
}
});
+ }
+
+ private void setupNavigationView(List<Layer> layerList) {
+ final LayerListAdapter adapter = new LayerListAdapter(this, layerList);
+ ListView listView = (ListView) findViewById(R.id.listView);
+ listView.setAdapter(adapter);
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Layer clickedLayer = adapter.getItem(position);
+ toggleLayerVisibility(clickedLayer);
+ closeNavigationView();
+ }
+ });
+ }
+ private void toggleLayerVisibility(Layer layer) {
+ boolean isVisible = layer.getVisibility().getValue().equals(Property.VISIBLE);
+ layer.setProperties(
+ visibility(
+ isVisible ? Property.NONE : Property.VISIBLE
+ )
+ );
+ }
+
+ private void closeNavigationView() {
+ DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ drawerLayout.closeDrawers();
+ }
+
+ private void setupZoomView() {
+ final TextView textView = (TextView) findViewById(R.id.textZoom);
+ mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
+ @Override
+ public void onCameraChange(CameraPosition position) {
+ textView.setText(String.format(getString(R.string.debug_zoom), position.zoom));
+ }
+ });
+ }
+ private void setupDebugChangeView() {
FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug);
fabDebug.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mapboxMap != null) {
- Timber.d("Debug FAB: isDebug Active? " + mapboxMap.isDebugActive());
+ Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive());
mapboxMap.cycleDebugOptions();
}
}
});
+ }
+ private void setupStyleChangeView() {
FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles);
fabStyles.setOnClickListener(new View.OnClickListener() {
@Override
@@ -96,6 +191,11 @@ public class DebugModeActivity extends AppCompatActivity {
}
@Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
+ }
+
+ @Override
protected void onStart() {
super.onStart();
mapView.onStart();
@@ -136,4 +236,58 @@ public class DebugModeActivity extends AppCompatActivity {
super.onLowMemory();
mapView.onLowMemory();
}
+
+ private static class LayerListAdapter extends BaseAdapter {
+
+ private LayoutInflater layoutInflater;
+ private List<Layer> layers;
+
+ LayerListAdapter(Context context, List<Layer> layers) {
+ this.layoutInflater = LayoutInflater.from(context);
+ this.layers = layers;
+ }
+
+ @Override
+ public int getCount() {
+ return layers.size();
+ }
+
+ @Override
+ public Layer getItem(int position) {
+ return layers.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Layer layer = layers.get(position);
+ View view = convertView;
+ if (view == null) {
+ view = layoutInflater.inflate(android.R.layout.simple_list_item_2, parent, false);
+ ViewHolder holder = new ViewHolder(
+ (TextView) view.findViewById(android.R.id.text1),
+ (TextView) view.findViewById(android.R.id.text2)
+ );
+ view.setTag(holder);
+ }
+ ViewHolder holder = (ViewHolder) view.getTag();
+ holder.text.setText(layer.getClass().getSimpleName());
+ holder.subText.setText(layer.getId());
+ return view;
+ }
+
+ private static class ViewHolder {
+ final TextView text;
+ final TextView subText;
+
+ ViewHolder(TextView text, TextView subText) {
+ this.text = text;
+ this.subText = subText;
+ }
+ }
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
index 7ec9cb9242..462c0c8025 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
@@ -8,7 +8,6 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
-import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
@@ -23,6 +22,12 @@ import com.mapbox.mapboxsdk.maps.TrackingSettings;
import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.mapboxsdk.testapp.R;
+/**
+ * Test activity showcasing having 2 maps on top of each other.
+ * <p>
+ * The small map is using the `mapbox_enableZMediaOverlay="true"` configuration
+ * </p>
+ */
public class DoubleMapActivity extends AppCompatActivity {
private static final String TAG_FRAGMENT = "map";
@@ -45,11 +50,11 @@ public class DoubleMapActivity extends AppCompatActivity {
public void setMapboxMap(MapboxMap map) {
// we need to set mapboxmap on the parent activity,
// for auto-generated ui tests
-
mapboxMap = map;
mapboxMap.setStyleUrl(Style.DARK);
mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(18));
try {
+ mapboxMap.setMyLocationEnabled(true);
TrackingSettings settings = mapboxMap.getTrackingSettings();
settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
} catch (SecurityException securityException) {
@@ -58,6 +63,9 @@ public class DoubleMapActivity extends AppCompatActivity {
}
}
+ /**
+ * Custom fragment containing 2 MapViews.
+ */
public static class DoubleMapFragment extends Fragment {
private DoubleMapActivity activity;
@@ -107,6 +115,7 @@ public class DoubleMapActivity extends AppCompatActivity {
uiSettings.setLogoEnabled(false);
try {
+ mapboxMap.setMyLocationEnabled(true);
TrackingSettings settings = mapboxMap.getTrackingSettings();
settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
} catch (SecurityException securityException) {
@@ -123,9 +132,6 @@ public class DoubleMapActivity extends AppCompatActivity {
});
}
});
-
- SurfaceView surfaceViewMini = (SurfaceView) mapViewMini.findViewById(R.id.surfaceView);
- surfaceViewMini.setZOrderMediaOverlay(true);
}
@Override
@@ -157,8 +163,8 @@ public class DoubleMapActivity extends AppCompatActivity {
}
@Override
- public void onDestroy() {
- super.onDestroy();
+ public void onDestroyView() {
+ super.onDestroyView();
mapView.onDestroy();
mapViewMini.onDestroy();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java
index 9ac87deb0d..24a167e260 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java
@@ -16,7 +16,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
- * Test Activity showcasing restricting user gestures to a bounds around Iceland.
+ * Test activity showcasing restricting user gestures to a bounds around Iceland.
*/
public class LatLngBoundsForCameraActivity extends AppCompatActivity implements OnMapReadyCallback {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java
index 495360a168..f0827c65cc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java
@@ -1,6 +1,8 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+import android.app.Dialog;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
@@ -65,6 +67,22 @@ public class MapInDialogActivity extends AppCompatActivity {
mapView.onCreate(savedInstanceState);
}
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new Dialog(getActivity(), getTheme()) {
+ boolean destroyed = false;
+ @Override
+ public void dismiss() {
+ if (mapView != null && !destroyed) {
+ mapView.onDestroy();
+ destroyed = true;
+ }
+ super.dismiss();
+ }
+ };
+ }
+
@Override
public void onStart() {
super.onStart();
@@ -90,12 +108,6 @@ public class MapInDialogActivity extends AppCompatActivity {
}
@Override
- public void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java
deleted file mode 100644
index 888482b219..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java
+++ /dev/null
@@ -1,252 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.maplayout;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.support.design.widget.Snackbar;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapFragment;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.R;
-
-public class NavigationDrawerActivity extends AppCompatActivity {
-
- private boolean firstStyle = true;
- private MapboxMap mapboxMap;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_navigation_drawer);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- NavigationDrawerFragment navigationDrawerFragment;
-
- getFragmentManager()
- .beginTransaction()
- .add(R.id.navigation_drawer, navigationDrawerFragment = new NavigationDrawerFragment())
- .commit();
-
- navigationDrawerFragment.setUp(this,
- R.id.navigation_drawer,
- (DrawerLayout) findViewById(R.id.drawer_layout),
- getSupportActionBar());
- }
-
- public void onNavigationDrawerItemSelected(int position) {
- // update the main content by replacing fragments
- switch (position) {
- case 0:
- // options
- MapboxMapOptions options = new MapboxMapOptions();
- options.styleUrl(firstStyle ? Style.LIGHT : Style.SATELLITE);
- options.camera(new CameraPosition.Builder()
- .target(new LatLng(39.913271, 116.413891))
- .zoom(12)
- .build());
-
- // fragment
- MapFragment mapFragment = MapFragment.newInstance(options);
- FragmentManager fragmentManager = getFragmentManager();
- fragmentManager.beginTransaction()
- .replace(R.id.container, mapFragment)
- .commit();
-
- // get callback when map is ready
- mapFragment.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- }
- });
-
- firstStyle = !firstStyle;
- break;
- case 1:
- Snackbar.make(
- findViewById(android.R.id.content),
- "Hello from snackbar",
- Snackbar.LENGTH_LONG)
- .show();
- break;
- }
- }
-
- public static class NavigationDrawerFragment extends Fragment {
-
- private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
- private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";
-
- private ActionBarDrawerToggle drawerToggle;
-
- private DrawerLayout drawerLayout;
- private ListView drawerListView;
- private View fragmentContainerView;
-
- private int currentSelectedPosition = 0;
- private boolean fromSavedInstanceState;
- private boolean userLearnedDrawer;
-
- public NavigationDrawerFragment() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
- userLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
-
- if (savedInstanceState != null) {
- currentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
- fromSavedInstanceState = true;
- }
- selectItem(currentSelectedPosition);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setHasOptionsMenu(true);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- drawerListView = (ListView) inflater.inflate(
- R.layout.drawer_navigation_drawer, container, false);
- drawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- selectItem(position);
- }
- });
- drawerListView.setAdapter(new ArrayAdapter<>(
- inflater.getContext(),
- android.R.layout.simple_list_item_activated_1,
- android.R.id.text1,
- new String[] {
- "Different style",
- "Show snackbar"
- }));
- drawerListView.setItemChecked(currentSelectedPosition, true);
- return drawerListView;
- }
-
- /**
- * Users of this fragment must call this method to set up the navigation drawer interactions.
- *
- * @param fragmentId The android:id of this fragment in its activity's layout.
- * @param drawerLayout The DrawerLayout containing this fragment's UI.
- */
- public void setUp(Activity activity, int fragmentId, DrawerLayout drawerLayout, ActionBar actionBar) {
- fragmentContainerView = activity.findViewById(fragmentId);
- this.drawerLayout = drawerLayout;
- // drawerLayout.setScrimColor(Color.TRANSPARENT);
-
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setHomeButtonEnabled(true);
-
- drawerToggle = new ActionBarDrawerToggle(
- activity,
- NavigationDrawerFragment.this.drawerLayout,
- R.string.navigation_drawer_open,
- R.string.navigation_drawer_close
- ) {
- @Override
- public void onDrawerClosed(View drawerView) {
- super.onDrawerClosed(drawerView);
- if (!isAdded()) {
- return;
- }
- getActivity().invalidateOptionsMenu();
- }
-
- @Override
- public void onDrawerOpened(View drawerView) {
- super.onDrawerOpened(drawerView);
- if (!isAdded()) {
- return;
- }
-
- if (!userLearnedDrawer) {
- userLearnedDrawer = true;
- SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(getActivity());
- sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
- }
- getActivity().invalidateOptionsMenu();
- }
- };
-
- if (!userLearnedDrawer && !fromSavedInstanceState) {
- this.drawerLayout.openDrawer(fragmentContainerView);
- }
- this.drawerLayout.post(new Runnable() {
- @Override
- public void run() {
- drawerToggle.syncState();
- }
- });
- this.drawerLayout.setDrawerListener(drawerToggle);
- }
-
- private void selectItem(int position) {
- currentSelectedPosition = position;
- if (drawerListView != null) {
- drawerListView.setItemChecked(position, true);
- }
- if (drawerLayout != null) {
- drawerLayout.closeDrawer(fragmentContainerView);
- }
-
- Activity activity = getActivity();
- if (activity != null && activity instanceof NavigationDrawerActivity) {
- NavigationDrawerActivity navActivity = (NavigationDrawerActivity) activity;
- navActivity.onNavigationDrawerItemSelected(position);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(STATE_SELECTED_POSITION, currentSelectedPosition);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- drawerToggle.onConfigurationChanged(newConfig);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (drawerToggle.onOptionsItemSelected(item)) {
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
index 344e9e140a..5bffd4d930 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
@@ -12,7 +12,6 @@ import android.widget.Toast;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
@@ -75,8 +74,7 @@ public class OfflineActivity extends AppCompatActivity
// state of your app. This will override any checks performed via the ConnectivityManager.
// Mapbox.getInstance().setConnected(false);
Boolean connected = Mapbox.isConnected();
- Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Mapbox is connected: %b", connected));
+ Timber.d("Mapbox is connected: %s", connected);
// Set up map
mapView = (MapView) findViewById(R.id.mapView);
@@ -207,7 +205,7 @@ public class OfflineActivity extends AppCompatActivity
@Override
public void onError(String error) {
- Timber.e("Error: " + error);
+ Timber.e("Error: %s" , error);
}
});
}
@@ -223,7 +221,7 @@ public class OfflineActivity extends AppCompatActivity
}
// Start progress bar
- Timber.d("Download started: " + regionName);
+ Timber.d("Download started: %s", regionName);
startProgress();
// Definition
@@ -241,14 +239,14 @@ public class OfflineActivity extends AppCompatActivity
offlineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() {
@Override
public void onCreate(OfflineRegion offlineRegion) {
- Timber.d("Offline region created: " + regionName);
+ Timber.d("Offline region created: %s" , regionName);
OfflineActivity.this.offlineRegion = offlineRegion;
launchDownload();
}
@Override
public void onError(String error) {
- Timber.e("Error: " + error);
+ Timber.e("Error: %s", error);
}
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
new file mode 100644
index 0000000000..6b1cb920fc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
@@ -0,0 +1,131 @@
+package com.mapbox.mapboxsdk.testapp.activity.snapshot;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.ViewTreeObserver;
+import android.widget.GridLayout;
+import android.widget.ImageView;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import timber.log.Timber;
+
+/**
+ * Test activity showing how to use a the {@link com.mapbox.mapboxsdk.snapshotter.MapSnapshotter}
+ */
+public class MapSnapshotterActivity extends AppCompatActivity {
+
+ private GridLayout grid;
+ private List<MapSnapshotter> snapshotters = new ArrayList<>();
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_snapshotter);
+
+ // Find the grid view and start snapshotting as soon
+ // as the view is measured
+ grid = (GridLayout) findViewById(R.id.snapshot_grid);
+ grid.getViewTreeObserver()
+ .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ //noinspection deprecation
+ grid.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ addSnapshots();
+ }
+ });
+ }
+
+ private void addSnapshots() {
+ Timber.i("Creating snapshotters");
+
+ for (int row = 0; row < grid.getRowCount(); row++) {
+ for (int column = 0; column < grid.getColumnCount(); column++) {
+ startSnapShot(row, column);
+ }
+ }
+ }
+
+ private void startSnapShot(final int row, final int column) {
+
+ // Define the dimensions
+ MapSnapshotter.Options options = new MapSnapshotter.Options(
+ grid.getMeasuredWidth() / grid.getColumnCount(),
+ grid.getMeasuredHeight() / grid.getRowCount()
+ )
+ // Optionally the pixel ratio
+ .withPixelRatio(1)
+
+ // Optionally the style
+ .withStyle((column + row) % 2 == 0 ? Style.TRAFFIC_DAY : Style.DARK);
+
+ // Optionally the visible region
+ if (row % 2 == 0) {
+ options.withRegion(new LatLngBounds.Builder()
+ .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160)))
+ .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160)))
+ .build()
+ );
+ }
+
+ // Optionally the camera options
+ if (column % 2 == 0) {
+ options.withCameraPosition(new CameraPosition.Builder()
+ .target(options.getRegion() != null
+ ? options.getRegion().getCenter()
+ : new LatLng(randomInRange(-80, 80), randomInRange(-160, 160)))
+ .bearing(randomInRange(0, 360))
+ .tilt(randomInRange(0, 60))
+ .zoom(randomInRange(0, 20))
+ .build()
+ );
+ }
+
+ MapSnapshotter snapshotter = new MapSnapshotter(MapSnapshotterActivity.this, options);
+
+ snapshotter.start(new MapboxMap.SnapshotReadyCallback() {
+ @Override
+ public void onSnapshotReady(Bitmap snapshot) {
+ Timber.i("Got the snapshot");
+ ImageView imageView = new ImageView(MapSnapshotterActivity.this);
+ imageView.setImageBitmap(snapshot);
+ grid.addView(
+ imageView,
+ new GridLayout.LayoutParams(GridLayout.spec(row), GridLayout.spec(column))
+ );
+ }
+ });
+ snapshotters.add(snapshotter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ // Make sure to stop the snapshotters on pause
+ for (MapSnapshotter snapshotter : snapshotters) {
+ snapshotter.cancel();
+ }
+ snapshotters.clear();
+ }
+
+ private static Random random = new Random();
+
+ public static float randomInRange(float min, float max) {
+ return (random.nextFloat() * (max - min)) + min;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
index 2238d1d5fe..f99a1fdb96 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
@@ -74,7 +74,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
source = new GeoJsonSource("bus_stop",
new URL("https://raw.githubusercontent.com/cheeaun/busrouter-sg/master/data/2/bus-stops.geojson"));
} catch (MalformedURLException exception) {
- Timber.e("That's not an url... ", exception);
+ Timber.e(exception, "That's not an url... ");
}
mapboxMap.addSource(source);
}
@@ -132,7 +132,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
new URL("https://gist.githubusercontent.com/tobrun/7fbc0fe7e9ffea509f7608cda2601d5d/raw/"
+ "a4b8cc289020f91fa2e1553524820054395e36f5/line.geojson")));
} catch (MalformedURLException malformedUrlException) {
- Timber.e("That's not an url... ", malformedUrlException);
+ Timber.e(malformedUrlException, "That's not an url... ");
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
index 3a5b30f71f..571b48daa6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.RawRes;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -18,14 +17,9 @@ import com.mapbox.mapboxsdk.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
import timber.log.Timber;
@@ -355,7 +349,7 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
// Add a source
Source source;
try {
- source = new GeoJsonSource("amsterdam-parks-source", readRawResource(R.raw.amsterdam));
+ source = new GeoJsonSource("amsterdam-parks-source", ResourceUtils.readRawResource(this, R.raw.amsterdam));
mapboxMap.addSource(source);
} catch (IOException ioException) {
Toast.makeText(
@@ -375,21 +369,4 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
)
);
}
-
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- int numRead;
- while ((numRead = reader.read(buffer)) != -1) {
- writer.write(buffer, 0, numRead);
- }
- } finally {
- is.close();
- }
-
- return writer.toString();
- }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
index 1ff0b0e8e1..24914fcbb2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
-
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
@@ -9,6 +8,9 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+/**
+ * Test activity used for instrumentation tests of fill extrusion.
+ */
public class FillExtrusionStyleTestActivity extends AppCompatActivity {
public MapView mapView;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
index 80dfe777cb..a2111bc304 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
@@ -128,7 +128,7 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
)
);
} catch (MalformedURLException malformedUrlException) {
- Timber.e("That's not an url... " + malformedUrlException.getMessage());
+ Timber.e(malformedUrlException,"That's not an url... ");
}
// Add unclustered layer
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java
index b9f7ebce35..b9f1dfe810 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java
@@ -55,7 +55,7 @@ public class RealTimeGeoJsonActivity extends AppCompatActivity implements OnMapR
try {
mapboxMap.addSource(new GeoJsonSource(ID_GEOJSON_SOURCE, new URL(URL_GEOJSON_SOURCE)));
} catch (MalformedURLException malformedUrlException) {
- Timber.e("Invalid URL", malformedUrlException);
+ Timber.e(malformedUrlException, "Invalid URL");
}
// add layer
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java
index f6754af0f9..adce889007 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
-import android.support.annotation.RawRes;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -32,16 +31,11 @@ import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.mapboxsdk.style.sources.TileSet;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
import com.mapbox.services.commons.geojson.Feature;
import com.mapbox.services.commons.geojson.FeatureCollection;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -285,7 +279,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
// Add a source
Source source;
try {
- source = new GeoJsonSource("amsterdam-spots", readRawResource(R.raw.amsterdam));
+ source = new GeoJsonSource("amsterdam-spots", ResourceUtils.readRawResource(this, R.raw.amsterdam));
} catch (IOException ioException) {
Toast.makeText(
RuntimeStyleActivity.this,
@@ -317,12 +311,12 @@ public class RuntimeStyleActivity extends AppCompatActivity {
layer = mapboxMap.getLayerAs("parksLayer");
// And get some properties
PropertyValue<Boolean> fillAntialias = layer.getFillAntialias();
- Timber.d("Fill anti alias: " + fillAntialias.getValue());
+ Timber.d("Fill anti alias: %s", fillAntialias.getValue());
layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
PropertyValue<String> fillTranslateAnchor = layer.getFillTranslateAnchor();
- Timber.d("Fill translate anchor: " + fillTranslateAnchor.getValue());
+ Timber.d("Fill translate anchor: %s", fillTranslateAnchor.getValue());
PropertyValue<String> visibility = layer.getVisibility();
- Timber.d("Visibility: " + visibility.getValue());
+ Timber.d("Visibility: %s", visibility.getValue());
// Get a good look at it all
mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12));
@@ -332,7 +326,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
// Load some data
FeatureCollection parks;
try {
- String json = readRawResource(R.raw.amsterdam);
+ String json = ResourceUtils.readRawResource(this, R.raw.amsterdam);
parks = FeatureCollection.fromJson(json);
} catch (IOException ioException) {
Toast.makeText(
@@ -477,33 +471,16 @@ public class RuntimeStyleActivity extends AppCompatActivity {
Function<Float, String> function = (Function<Float, String>) fillColor.getFunction();
if (function != null) {
ExponentialStops<Float, String> stops = (ExponentialStops) function.getStops();
- Timber.d("Fill color base: " + stops.getBase());
- Timber.d("Fill color #stops: " + stops.size());
+ Timber.d("Fill color base: %s", stops.getBase());
+ Timber.d("Fill color #stops: %s", stops.size());
if (function.getStops() != null) {
for (Stop<Float, String> stop : stops) {
- Timber.d("Fill color #stops: " + stop);
+ Timber.d("Fill color #stops: %s", stop);
}
}
}
}
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- int numRead;
- while ((numRead = reader.read(buffer)) != -1) {
- writer.write(buffer, 0, numRead);
- }
- } finally {
- is.close();
- }
-
- return writer.toString();
- }
-
private void addCustomTileSource() {
// Add a source
Source source = new VectorSource("custom-tile-source", new TileSet("2.1.0", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.mvt?api_key=vector-tiles-LM25tq4"));
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java
index 6906e90f6e..49015ec1e9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java
@@ -1,9 +1,9 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
+import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
-import android.support.annotation.RawRes;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
@@ -14,22 +14,18 @@ import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
+import java.lang.ref.WeakReference;
import timber.log.Timber;
/**
- * Test activity showcasing how to use a file:// resource for the style.json
+ * Test activity showcasing how to use a file:// resource for the style.json and how to use MapboxMap#setStyleJson.
*/
public class StyleFileActivity extends AppCompatActivity {
@@ -48,13 +44,21 @@ public class StyleFileActivity extends AppCompatActivity {
public void onMapReady(@NonNull final MapboxMap map) {
mapboxMap = map;
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_file);
fab.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- Timber.i("Loading style file");
- new CreateStyleFileTask().execute();
+ new CreateStyleFileTask(view.getContext(), mapboxMap).execute();
+ }
+ });
+
+ FloatingActionButton fabStyleJson = (FloatingActionButton) findViewById(R.id.fab_style_json);
+ fabStyleJson.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
+ fabStyleJson.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new LoadStyleFileTask(view.getContext(), mapboxMap).execute();
}
});
}
@@ -62,44 +66,74 @@ public class StyleFileActivity extends AppCompatActivity {
}
/**
+ * Task to read a style file from the raw folder
+ */
+ private static class LoadStyleFileTask extends AsyncTask<Void, Void, String> {
+ private WeakReference<Context> context;
+ private WeakReference<MapboxMap> mapboxMap;
+
+ LoadStyleFileTask(Context context, MapboxMap mapboxMap) {
+ this.context = new WeakReference<>(context);
+ this.mapboxMap = new WeakReference<>(mapboxMap);
+ }
+
+ @Override
+ protected String doInBackground(Void... voids) {
+ String styleJson = "";
+ try {
+ styleJson = ResourceUtils.readRawResource(context.get(), R.raw.sat_style);
+ } catch (Exception exception) {
+ Timber.e(exception, "Can't load local file style");
+ }
+ return styleJson;
+ }
+
+ @Override
+ protected void onPostExecute(String json) {
+ super.onPostExecute(json);
+ Timber.d("Read json, %s", json);
+ MapboxMap mapboxMap = this.mapboxMap.get();
+ if (mapboxMap != null) {
+ mapboxMap.setStyleJson(json);
+ }
+ }
+ }
+
+ /**
* Task to write a style file to local disk and load it in the map view
*/
- private class CreateStyleFileTask extends AsyncTask<Void, Integer, Long> {
+ private static class CreateStyleFileTask extends AsyncTask<Void, Integer, Long> {
private File cacheStyleFile;
+ private WeakReference<Context> context;
+ private WeakReference<MapboxMap> mapboxMap;
+
+ CreateStyleFileTask(Context context, MapboxMap mapboxMap) {
+ this.context = new WeakReference<>(context);
+ this.mapboxMap = new WeakReference<>(mapboxMap);
+ }
@Override
protected Long doInBackground(Void... params) {
try {
cacheStyleFile = File.createTempFile("my-", ".style.json");
cacheStyleFile.createNewFile();
- Timber.i("Writing style file to: " + cacheStyleFile.getAbsolutePath());
- writeToFile(cacheStyleFile, readRawResource(R.raw.local_style));
+ Timber.i("Writing style file to: %s", cacheStyleFile.getAbsolutePath());
+ Context context = this.context.get();
+ if (context != null) {
+ writeToFile(cacheStyleFile, ResourceUtils.readRawResource(context, R.raw.local_style));
+ }
} catch (Exception exception) {
- Toast.makeText(StyleFileActivity.this, "Could not create style file in cache dir", Toast.LENGTH_SHORT).show();
+ Toast.makeText(context.get(), "Could not create style file in cache dir", Toast.LENGTH_SHORT).show();
}
return 1L;
}
protected void onPostExecute(Long result) {
// Actual file:// usage
- mapboxMap.setStyleUrl("file://" + cacheStyleFile.getAbsolutePath());
- }
-
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- int numRead;
- while ((numRead = reader.read(buffer)) != -1) {
- writer.write(buffer, 0, numRead);
- }
- } finally {
- is.close();
+ MapboxMap mapboxMap = this.mapboxMap.get();
+ if (mapboxMap != null) {
+ mapboxMap.setStyleUrl("file://" + cacheStyleFile.getAbsolutePath());
}
-
- return writer.toString();
}
private void writeToFile(File file, String contents) throws IOException {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
new file mode 100644
index 0000000000..0eaccfef0c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
@@ -0,0 +1,216 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v7.app.AppCompatActivity;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.gson.GsonBuilder;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.style.sources.Source;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
+import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+import com.mapbox.services.commons.geojson.Geometry;
+import com.mapbox.services.commons.geojson.custom.GeometryDeserializer;
+import com.mapbox.services.commons.geojson.custom.PositionDeserializer;
+import com.mapbox.services.commons.models.Position;
+
+import java.io.IOException;
+
+import java.util.List;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+
+/**
+ * Test activity showcasing using a symbol generator that generates Bitmaps from Android SDK Views.
+ */
+public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private static final String SOURCE_ID = "com.mapbox.mapboxsdk.style.layers.symbol.source.id";
+ private static final String LAYER_ID = "com.mapbox.mapboxsdk.style.layers.symbol.layer.id";
+ private static final String FEATURE_ID = "brk_name";
+ private static final String FEATURE_VALUE = "name_sort";
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_symbol_generator);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ try {
+ // read local geojson from raw folder
+ String tinyCountriesJson = ResourceUtils.readRawResource(this, R.raw.tiny_countries);
+
+ // convert geojson to a model
+ FeatureCollection featureCollection = new GsonBuilder()
+ .registerTypeAdapter(Geometry.class, new GeometryDeserializer())
+ .registerTypeAdapter(Position.class, new PositionDeserializer())
+ .create().fromJson(tinyCountriesJson, FeatureCollection.class);
+
+ // add a geojson to the map
+ Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+
+ // for each feature add a symbolLayer
+ for (Feature feature : featureCollection.getFeatures()) {
+ String countryName = feature.getStringProperty(FEATURE_ID);
+
+ // create View
+ TextView textView = new TextView(this);
+ textView.setBackgroundColor(getResources().getColor(R.color.blueAccent));
+ textView.setPadding(10, 5, 10, 5);
+ textView.setTextColor(Color.WHITE);
+ textView.setText(countryName);
+
+ // create bitmap from view
+ mapboxMap.addImage(countryName, SymbolGenerator.generate(textView));
+ }
+
+ // create layer use
+ mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
+ .withProperties(
+ iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
+ iconAllowOverlap(false)
+ )
+ );
+
+ addSymbolClickListener();
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
+ }
+
+ private void addSymbolClickListener() {
+ mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
+ List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
+ if (!features.isEmpty()) {
+ Feature feature = features.get(0);
+ Timber.v("Feature was clicked with data: %s", feature.toJson());
+ Toast.makeText(
+ SymbolGeneratorActivity.this,
+ "hello from: " + feature.getStringProperty(FEATURE_VALUE),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_generator_symbol, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_action_icon_overlap) {
+ SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID);
+ layer.setProperties(iconAllowOverlap(!layer.getIconAllowOverlap().getValue()));
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ /**
+ * Utility class to generate Bitmaps for Symbol.
+ * <p>
+ * Bitmaps can be added to the map with {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}
+ * </p>
+ */
+ private static class SymbolGenerator {
+
+ /**
+ * Generate a Bitmap from an Android SDK View.
+ *
+ * @param view the View to be drawn to a Bitmap
+ * @return the generated bitmap
+ */
+ public static Bitmap generate(@NonNull View view) {
+ int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ view.measure(measureSpec, measureSpec);
+
+ int measuredWidth = view.getMeasuredWidth();
+ int measuredHeight = view.getMeasuredHeight();
+
+ view.layout(0, 0, measuredWidth, measuredHeight);
+ Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
+ bitmap.eraseColor(Color.TRANSPARENT);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ return bitmap;
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
new file mode 100644
index 0000000000..95c3929c1d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
@@ -0,0 +1,196 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.google.gson.JsonObject;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+import com.mapbox.services.commons.geojson.Point;
+import com.mapbox.services.commons.models.Position;
+
+import java.util.List;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.style.functions.Function.property;
+import static com.mapbox.mapboxsdk.style.functions.Function.zoom;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stops.categorical;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
+
+/**
+ * Test activity showcasing changing the icon with a zoom function and adding selection state to a SymbolLayer.
+ */
+public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
+
+ private static final String LAYER_ID = "symbolLayer";
+ private static final String SOURCE_ID = "poiSource";
+ private static final String BUS_MAKI_ICON_ID = "bus-11";
+ private static final String CAFE_MAKI_ICON_ID = "cafe-11";
+ private static final String KEY_PROPERTY_SELECTED = "selected";
+ private static final float ZOOM_STOP_MIN_VALUE = 7.0f;
+ private static final float ZOOM_STOP_MAX_VALUE = 12.0f;
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private GeoJsonSource source;
+
+ private boolean isInitialPosition = true;
+ private boolean isSelected = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_zoom_symbol_layer);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap map) {
+ mapboxMap = map;
+ updateSource();
+ addLayer();
+ addMapClickListener();
+ }
+ });
+ }
+
+ private void updateSource() {
+ FeatureCollection featureCollection = createFeatureCollection();
+ if (source != null) {
+ source.setGeoJson(featureCollection);
+ } else {
+ source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+ }
+ }
+
+ private FeatureCollection createFeatureCollection() {
+ Position position = isInitialPosition
+ ? Position.fromCoordinates(-74.01618140, 40.701745)
+ : Position.fromCoordinates(-73.988097, 40.749864);
+
+ Point point = Point.fromCoordinates(position);
+ Feature feature = Feature.fromGeometry(point);
+ JsonObject properties = new JsonObject();
+ properties.addProperty(KEY_PROPERTY_SELECTED, isSelected);
+ feature.setProperties(properties);
+ return FeatureCollection.fromFeatures(new Feature[] {feature});
+ }
+
+ private void addLayer() {
+ SymbolLayer layer = new SymbolLayer(LAYER_ID, SOURCE_ID);
+ layer.setProperties(
+ iconImage(
+ zoom(
+ interval(
+ stop(ZOOM_STOP_MIN_VALUE, iconImage(BUS_MAKI_ICON_ID)),
+ stop(ZOOM_STOP_MAX_VALUE, iconImage(CAFE_MAKI_ICON_ID))
+ )
+ )
+ ),
+ iconSize(
+ property(
+ KEY_PROPERTY_SELECTED,
+ categorical(
+ stop(true, iconSize(3.0f)),
+ stop(false, iconSize(1.0f))
+ )
+ )
+ ),
+ iconAllowOverlap(true)
+ );
+ mapboxMap.addLayer(layer);
+ }
+
+ private void addMapClickListener() {
+ mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point);
+ List<Feature> featureList = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID);
+ if (!featureList.isEmpty()) {
+ Feature feature = featureList.get(0);
+ boolean selectedNow = feature.getBooleanProperty(KEY_PROPERTY_SELECTED);
+ isSelected = !selectedNow;
+ updateSource();
+ } else {
+ Timber.e("No features found");
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_symbols, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (mapboxMap != null && item.getItemId() == R.id.menu_action_change_location) {
+ isInitialPosition = !isInitialPosition;
+ updateSource();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
index f41e5e38f0..71b8115d2e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
@@ -14,6 +14,9 @@ import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
import java.util.List;
+/**
+ * Base class for location aware activities.
+ */
public abstract class BaseLocationActivity extends AppCompatActivity implements PermissionsListener {
private PermissionsManager permissionsManager;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
index b0ea9c608b..e954b73f8d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
@@ -12,6 +12,9 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+/**
+ * Test activity showcasing using a custom location engine.
+ */
public class CustomLocationEngineActivity extends BaseLocationActivity {
private MapView mapView;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
index 69e6d64325..9f6f2b2fcd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
@@ -50,7 +50,7 @@ public class MyLocationDrawableActivity extends BaseLocationActivity implements
mapView = new MapView(this, mapboxMapOptions);
mapView.setId(R.id.mapView);
- ViewGroup parent = (ViewGroup) findViewById(R.id.container);
+ ViewGroup parent = (ViewGroup) findViewById(android.R.id.content);
parent.addView(mapView);
mapView.onCreate(savedInstanceState);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
index d465d676f7..718c10c7cb 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
@@ -11,6 +11,9 @@ import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
+/**
+ * Test activity showcasing toggling the user location on the map.
+ */
public class MyLocationToggleActivity extends BaseLocationActivity {
private MapView mapView;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
index 8c049d7730..aaad2f04ab 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
@@ -11,5 +11,6 @@ public class ExampleCustomLayer {
public static long InitializeFunction;
public static long RenderFunction;
+ public static long ContextLostFunction;
public static long DeinitializeFunction;
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
index f717daeada..76f07ba526 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
@@ -32,7 +32,7 @@ public class OfflineListRegionsDialog extends DialogFragment {
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- Timber.d("Selected item: " + which);
+ Timber.d("Selected item: %s", which);
}
})
.setPositiveButton("Accept", new DialogInterface.OnClickListener() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java
index 8c6ab3e211..6220dc7e69 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java
@@ -29,7 +29,7 @@ public class OfflineUtils {
String json = jsonObject.toString();
metadata = json.getBytes(JSON_CHARSET);
} catch (Exception exception) {
- Timber.e("Failed to encode metadata: " + exception.getMessage());
+ Timber.e(exception, "Failed to encode metadata: ");
}
return metadata;
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java
new file mode 100644
index 0000000000..f0cca57e10
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java
@@ -0,0 +1,36 @@
+package com.mapbox.mapboxsdk.testapp.utils;
+
+import android.content.Context;
+import android.support.annotation.RawRes;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+public class ResourceUtils {
+
+ public static String readRawResource(Context context, @RawRes int rawResource) throws IOException {
+ String json = "";
+ if (context != null) {
+ InputStream is = context.getResources().openRawResource(rawResource);
+ Writer writer = new StringWriter();
+ char[] buffer = new char[1024];
+ try {
+ Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+ int numRead;
+ while ((numRead = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, numRead);
+ }
+ } finally {
+ is.close();
+ }
+ json = writer.toString();
+ }
+ return json;
+ }
+}
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
index 6f20d6fb0f..235fd8233b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
@@ -146,15 +146,15 @@ public class TimingLogger {
if (disabled) {
return;
}
- Timber.d(label + ": begin");
+ Timber.d("%s: begin", label);
final long first = splits.get(0);
long now = first;
for (int i = 1; i < splits.size(); i++) {
now = splits.get(i);
final String splitLabel = splitLabels.get(i);
final long prev = splits.get(i - 1);
- Timber.d(label + ": " + (now - prev) + " ms, " + splitLabel);
+ Timber.d("%s: %s ms, %s", label, (now - prev), splitLabel);
}
- Timber.d(label + ": end, " + (now - first) + " ms");
+ Timber.d("%s: end, %s ms", label, (now - first));
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java
new file mode 100644
index 0000000000..a69fb48ab4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java
@@ -0,0 +1,74 @@
+package com.mapbox.mapboxsdk.testapp.view;
+
+import android.content.Context;
+import android.support.design.widget.BottomSheetBehavior;
+import android.support.design.widget.CoordinatorLayout;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class LockableBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
+
+ private boolean locked = false;
+
+ public LockableBottomSheetBehavior(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onInterceptTouchEvent(parent, child, event);
+ }
+ return handled;
+ }
+
+ @Override
+ public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onTouchEvent(parent, child, event);
+ }
+ return handled;
+ }
+
+ @Override
+ public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target,
+ int nestedScrollAxes) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
+ }
+ return handled;
+ }
+
+ @Override
+ public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy,
+ int[] consumed) {
+ if (!locked) {
+ super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
+ }
+ }
+
+ @Override
+ public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
+ if (!locked) {
+ super.onStopNestedScroll(coordinatorLayout, child, target);
+ }
+ }
+
+ @Override
+ public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX,
+ float velocityY) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
+ }
+ return handled;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml
new file mode 100644
index 0000000000..ded53fc4f2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"
+ android:fillColor="#F1F1F1"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
deleted file mode 100644
index 3217661b64..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
-
- <solid
- android:color="@color/mapbox_blue"/>
-
- <size
- android:width="@dimen/circle_size"
- android:height="@dimen/circle_size"/>
-</shape>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml
new file mode 100644
index 0000000000..bf9b895aca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml
deleted file mode 100644
index 71992ebd7f..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <size android:width="10px" android:height="10px"/>
- <solid android:color="@color/redAccent"/>
-</shape> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml
deleted file mode 100644
index 6da5a61ecb..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
-</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml
index ac1f08e821..26b40b9ab6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml
@@ -5,25 +5,13 @@
android:layout_height="match_parent"
android:orientation="vertical">
-<com.mapbox.mapboxsdk.maps.MapView
- android:id="@id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:mapbox_cameraTargetLat="46.437"
- app:mapbox_cameraTargetLng="-80.425"
- app:mapbox_cameraZoom="3"
- app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
-
-<android.support.design.widget.FloatingActionButton
- android:id="@+id/fabStartStop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_marginBottom="@dimen/fab_margin"
- android:layout_marginRight="@dimen/fab_margin"
- android:src="@android:drawable/ic_media_play"
- app:backgroundTint="@color/primary"/>
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="41.9567"
+ app:mapbox_cameraTargetLng="-78.6430"
+ app:mapbox_cameraZoom="5"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml
index fa37c485d7..c8752df1bf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
@@ -20,8 +18,8 @@
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginBottom="82dp"
- android:layout_marginRight="@dimen/fab_margin"
android:layout_marginEnd="@dimen/fab_margin"
+ android:layout_marginRight="@dimen/fab_margin"
android:src="@drawable/ic_my_location"
app:backgroundTint="@color/accent"
app:layout_anchorGravity="top"/>
@@ -35,4 +33,4 @@
android:src="@drawable/ic_paint"
app:backgroundTint="@color/primary"/>
-</FrameLayout>
+</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
index b70bb6d7b2..0cb065a676 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
@@ -15,7 +15,9 @@
app:mapbox_cameraZoom="15"/>
<LinearLayout
+ style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
+ android:background="@color/primary"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
@@ -23,6 +25,7 @@
<Button
android:id="@+id/cameraMoveButton"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -30,6 +33,7 @@
<Button
android:id="@+id/cameraEaseButton"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -37,6 +41,7 @@
<Button
android:id="@+id/cameraAnimateButton"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml
new file mode 100644
index 0000000000..d4933bfb9a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/coordinator_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="37.774913"
+ app:mapbox_cameraTargetLng="-122.419368"
+ app:mapbox_cameraZoom="11"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_play_arrow_black_24dp"
+ app:backgroundTint="@android:color/white"/>
+
+</android.support.design.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml
index 6e8a4e5eb2..5ac55e75e2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml
@@ -23,6 +23,7 @@
android:layout_alignParentRight="true"
android:layout_marginBottom="@dimen/fab_margin"
android:layout_marginRight="@dimen/fab_margin"
+ android:layout_marginEnd="@dimen/fab_margin"
android:src="@drawable/ic_directions_bus_black"
app:backgroundTint="@android:color/white"/>
@@ -36,6 +37,7 @@
android:layout_marginBottom="@dimen/fab_margin"
android:layout_marginRight="@dimen/fab_margin"
android:src="@drawable/ic_layers"
+ android:layout_marginEnd="@dimen/fab_margin"
app:backgroundTint="@color/primary"/>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
index 6db8b073d9..c6f3c0e3f2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
@@ -1,46 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout
+<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/coordinator_layout"
+ android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:clickable="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true">
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
+ <android.support.design.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:mapbox_uiAttribution="false"
- app:mapbox_uiCompass="false"
- app:mapbox_uiLogo="false"/>
+ android:orientation="vertical">
- <TextView
- android:id="@+id/textZoom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|start"
- android:layout_margin="8dp"
- android:textSize="14sp"/>
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_uiAttribution="false"
+ app:mapbox_uiCompass="false"
+ app:mapbox_uiLogo="false"/>
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/fabDebug"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_marginBottom="82dp"
- android:layout_marginRight="@dimen/fab_margin"
- android:src="@drawable/ic_refresh"
- app:backgroundTint="@color/accent"
- app:layout_anchorGravity="top"/>
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/fabStyles"
+ <TextView
+ android:id="@+id/textZoom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|start"
+ android:layout_margin="8dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="bottom|start"/>
+
+ <TextView
+ android:id="@+id/fpsView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:layout_margin="8dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"
+ app:layout_anchor="@id/textZoom"
+ app:layout_anchorGravity="top"/>
+
+ <android.support.v4.widget.NestedScrollView
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="250dp"
+ android:background="@color/white"
+ android:visibility="invisible"
+ app:behavior_hideable="true"
+ app:layout_behavior="android.support.design.widget.BottomSheetBehavior"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabDebug"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:layout_marginBottom="82dp"
+ android:layout_marginEnd="@dimen/fab_margin"
+ android:layout_marginRight="@dimen/fab_margin"
+ android:src="@drawable/ic_refresh"
+ app:backgroundTint="@color/accent"
+ app:layout_anchor="@+id/fabStyles"
+ app:layout_anchorGravity="top"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@id/fabStyles"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_layers"
+ app:backgroundTint="@color/primary"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="bottom|end"/>
+
+ </android.support.design.widget.CoordinatorLayout>
+
+ <android.support.design.widget.NavigationView
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_margin="@dimen/fab_margin"
- android:src="@drawable/ic_layers"
- app:backgroundTint="@color/primary"/>
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:background="@android:color/white">
+
+ <ListView
+ android:id="@+id/listView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </android.support.design.widget.NavigationView>
-</android.support.design.widget.CoordinatorLayout>
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml
new file mode 100644
index 0000000000..e565c3c9d1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/coordinator_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ android:orientation="vertical">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+
+ <android.support.v4.widget.NestedScrollView
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="375dp"
+ android:background="@color/primaryDark"
+ app:behavior_hideable="true"
+ app:behavior_skipCollapsed="false"
+ app:layout_behavior="com.mapbox.mapboxsdk.testapp.view.LockableBottomSheetBehavior"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_arrow_downward"
+ app:backgroundTint="@color/primary"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="top|end"/>
+
+</android.support.design.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
index 43fa8fb995..419660b36a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
@@ -1,17 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/fragment_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context=".activity.fragment.MapFragmentActivity">
-
- <FrameLayout
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
-</LinearLayout>
+ android:layout_height="match_parent"/>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml
index 3fd66977e2..0a12fd9e50 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml
@@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/button_open_dialog"
@@ -11,4 +8,4 @@
android:layout_gravity="center"
android:text="@string/button_open_dialog"/>
-</FrameLayout>
+</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
index a0de31ee48..cd4aa4bdef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
@@ -1,19 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"/>
<View
android:layout_width="@dimen/map_padding_left"
android:layout_height="match_parent"
android:alpha="0.5"
- android:background="@color/mapbox_blue" />
+ android:background="@color/mapbox_blue"/>
<View
android:layout_width="match_parent"
@@ -24,13 +21,13 @@
android:layout_marginRight="@dimen/map_padding_right"
android:layout_marginStart="@dimen/map_padding_left"
android:alpha="0.5"
- android:background="@color/mapbox_blue" />
+ android:background="@color/mapbox_blue"/>
<View
android:layout_width="@dimen/map_padding_right"
android:layout_height="match_parent"
android:layout_gravity="end"
android:alpha="0.5"
- android:background="@color/mapbox_blue" />
+ android:background="@color/mapbox_blue"/>
-</FrameLayout>
+</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml
new file mode 100644
index 0000000000..30ad494dca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <GridLayout
+ android:id="@+id/snapshot_grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:columnCount="3"
+ android:orientation="horizontal"
+ android:rowCount="3"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
index ff28d2edf0..52691a26fe 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
@@ -17,6 +17,7 @@
<TextView
android:id="@+id/countView"
+ android:textIsSelectable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/toolbar"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
index cf4b51bbe0..dae3a1d9b7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
@@ -10,16 +10,18 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
+ android:textIsSelectable="false"
app:mapbox_cameraTargetLat="38.907192"
app:mapbox_cameraTargetLng="-77.036871"
- app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"
- app:mapbox_cameraZoom="12" />
+ app:mapbox_cameraZoom="12"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
<TextView
android:id="@+id/countView"
+ android:textIsSelectable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
- android:textSize="20sp" />
+ android:textSize="20sp"/>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml
index 084675fb2c..1eb999caf5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml
@@ -15,7 +15,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
- android:text="No Results"
+ android:text="@string/no_results"
android:textSize="24sp"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml
index 599ae3fa1c..193ae55e59 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml
@@ -2,13 +2,13 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/tools"
- android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:orientation="vertical"
+ mapbox:ignore="NestedWeights">
<LinearLayout
- android:id="@+id/map_container1"
+ android:baselineAligned="false"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
@@ -40,7 +40,7 @@
</LinearLayout>
<LinearLayout
- android:id="@+id/map_container2"
+ android:baselineAligned="false"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml
index d65d5796f1..addfe8427b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml
@@ -1,22 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <FrameLayout
+ <android.support.v4.widget.ContentLoadingProgressBar
android:id="@id/progress"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
- <android.support.v4.widget.ContentLoadingProgressBar
- style="?android:attr/progressBarStyleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"/>
-
- </FrameLayout>
-
-</LinearLayout>
+</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml
deleted file mode 100644
index 26a71bc568..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/drawer_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.mapbox.mapboxsdk.testapp.activity.maplayout.NavigationDrawerActivity">
-
- <FrameLayout
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="56dp" />
-
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/navigation_drawer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="start"
- android:fitsSystemWindows="true" />
-
-</android.support.v4.widget.DrawerLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
index d4b64b1ea2..3e21015e96 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
@@ -18,27 +18,30 @@
android:layout_height="match_parent"
android:layout_below="@+id/progress_bar"/>
- <Button
- android:id="@+id/button_download_region"
- android:layout_width="wrap_content"
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_margin="@dimen/fab_margin"
- android:background="@color/white"
- android:padding="10dp"
- android:text="@string/button_download_region"/>
+ android:background="@color/primary"
+ android:orientation="horizontal"
+ android:weightSum="2">
- <Button
- android:id="@+id/button_list_regions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@+id/button_download_region"
- android:layout_alignParentRight="true"
- android:layout_marginRight="@dimen/fab_margin"
- android:background="@color/white"
- android:padding="10dp"
- android:text="@string/button_list_regions"/>
+ <Button
+ android:id="@+id/button_download_region"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_download_region"/>
+
+ <Button
+ android:id="@+id/button_list_regions"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_list_regions"/>
+ </LinearLayout>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml
index 084675fb2c..1eb999caf5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml
@@ -15,7 +15,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
- android:text="No Results"
+ android:text="@string/no_results"
android:textSize="24sp"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
index bc24533960..cfbd07ce39 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
@@ -22,7 +23,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:paddingBottom="8dp"
- android:text="Move the map by x/y pixels"
+ android:text="@string/action_scroll_by"
android:textColor="#FFFFFF"
android:textSize="20sp" />
@@ -32,7 +33,8 @@
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/seekbar_move_x"
android:layout_below="@id/title"
- android:text="X: 0000" />
+ android:text="X: 0000"
+ tools:ignore="HardcodedText"/>
<SeekBar
android:id="@id/seekbar_move_x"
@@ -40,6 +42,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_marginLeft="56dp"
+ android:layout_marginStart="56dp"
android:max="50"
android:progress="0" />
@@ -48,7 +51,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/seekbar_move_y"
- android:text="Y: 0000" />
+ android:text="Y: 0000"
+ tools:ignore="HardcodedText"/>
<SeekBar
android:id="@id/seekbar_move_y"
@@ -58,6 +62,7 @@
android:layout_marginBottom="8dp"
android:layout_marginLeft="56dp"
android:layout_marginTop="16dp"
+ android:layout_marginStart="56dp"
android:max="50"
android:progress="0" />
@@ -66,7 +71,6 @@
</android.support.v7.widget.Toolbar>
<FrameLayout
- android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar">
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
index 6b99711e84..53345571b4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
@@ -16,6 +16,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
+ android:contentDescription="@null"
android:background="@color/primary"/>
<com.mapbox.mapboxsdk.maps.MapView
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml
index b133f3d9a5..83150be4bf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml
@@ -15,7 +15,18 @@
app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
<android.support.design.widget.FloatingActionButton
- android:id="@id/fab"
+ android:id="@+id/fab_style_json"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_above="@+id/fab_file"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_add"
+ app:backgroundTint="@android:color/white"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@id/fab_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml
index 89a28a7799..ffcdddce57 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -8,6 +9,7 @@
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="match_parent"
+ app:mapbox_styleUrl="@string/mapbox_style_outdoors"/>
-</LinearLayout>
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml
index 3b96277d23..3edaff6985 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml
@@ -10,7 +10,6 @@
android:layout_height="match_parent">
<android.support.v4.view.PagerTabStrip
- android:id="@+id/viewpager_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml
new file mode 100644
index 0000000000..0bb59451ab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="40.730648"
+ app:mapbox_cameraTargetLng="-73.993619"
+ app:mapbox_cameraZoom="11"
+ app:mapbox_styleUrl="@string/mapbox_style_light"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
index 1c9fbbd482..a7f422f9ce 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
@@ -16,7 +16,7 @@
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Latitude"
+ android:text="@string/latitude"
android:textColor="@android:color/white" />
<SeekBar
@@ -25,7 +25,9 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_lat"
android:layout_toLeftOf="@+id/value_lat"
+ android:layout_toEndOf="@+id/text_lat"
android:layout_toRightOf="@id/text_lat"
android:max="360" />
@@ -38,7 +40,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="-180"
+ android:text="@string/min_value"
android:textColor="@android:color/white" />
</RelativeLayout>
@@ -56,7 +58,7 @@
android:textColor="@android:color/white"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Longitude" />
+ android:text="@string/longitude" />
<SeekBar
android:id="@+id/seekbar_lon"
@@ -64,8 +66,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_lon"
android:layout_toLeftOf="@+id/value_lon"
android:layout_toRightOf="@id/text_lon"
+ android:layout_toEndOf="@id/text_lon"
android:max="360" />
<TextView
@@ -78,7 +82,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="-180" />
+ android:text="@string/min_value" />
</RelativeLayout>
@@ -95,7 +99,7 @@
android:textColor="@android:color/white"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Zoom" />
+ android:text="@string/zoom" />
<SeekBar
android:id="@+id/seekbar_zoom"
@@ -103,8 +107,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_zoom"
android:layout_toLeftOf="@+id/value_zoom"
android:layout_toRightOf="@id/text_zoom"
+ android:layout_toEndOf="@+id/text_zoom"
android:max="18" />
<TextView
@@ -117,7 +123,7 @@
android:textColor="@android:color/white"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="18" />
+ android:text="@string/default_zoom_value" />
</RelativeLayout>
@@ -134,7 +140,7 @@
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Bearing" />
+ android:text="@string/bearing" />
<SeekBar
android:id="@+id/seekbar_bearing"
@@ -142,8 +148,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_bearing"
android:layout_toLeftOf="@+id/value_bearing"
android:layout_toRightOf="@id/text_bearing"
+ android:layout_toEndOf="@id/text_bearing"
android:max="360" />
<TextView
@@ -156,7 +164,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="0" />
+ android:text="@string/default_tilt_value" />
</RelativeLayout>
@@ -173,7 +181,7 @@
android:textColor="@android:color/white"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Tilt" />
+ android:text="@string/tilt" />
<SeekBar
android:id="@+id/seekbar_tilt"
@@ -181,8 +189,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_tilt"
android:layout_toLeftOf="@+id/value_tilt"
android:layout_toRightOf="@id/text_tilt"
+ android:layout_toEndOf="@id/text_tilt"
android:max="60" />
<TextView
@@ -195,7 +205,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="0" />
+ android:text="@string/default_tilt_value" />
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml
index afebfa1c47..b8ea3d847e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml
@@ -12,7 +12,6 @@
mapbox:mapbox_cameraTargetLat="47.6077"
mapbox:mapbox_cameraTargetLng="-122.3421"
mapbox:mapbox_cameraZoom="11"
- mapbox:mapbox_renderTextureMode="true"
mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v10" />
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
index b976013ead..3cf2fbea55 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:maps="http://schemas.android.com/apk/res-auto">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
@@ -9,7 +10,6 @@
android:layout_height="match_parent" />
<FrameLayout
- android:id="@+id/map_card"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="5dp"
@@ -19,6 +19,7 @@
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mini_map"
android:layout_width="100dp"
+ maps:mapbox_enableZMediaOverlay="true"
android:layout_height="100dp" />
</FrameLayout>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
index b290d013f0..49b38f081a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
@@ -20,6 +20,7 @@
android:gravity="center_vertical"
android:maxLines="1"
android:textColor="@android:color/black"
+ android:textIsSelectable="false"
android:textSize="16sp"/>
<TextView
@@ -31,6 +32,7 @@
android:alpha="0.56"
android:maxLines="1"
android:textColor="@android:color/black"
+ android:textIsSelectable="false"
android:textSize="14sp"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
index 75f6ac9588..afec1f3bea 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
@@ -18,6 +18,7 @@
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
+ android:textIsSelectable="false"
android:layout_marginTop="16dp"
android:alpha="0.54"
android:background="@android:color/transparent"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
index 324a4861c3..cafa7d9746 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
@@ -6,6 +6,7 @@
<ImageView
android:id="@id/imageView"
android:layout_width="64dp"
+ android:contentDescription="@null"
android:layout_height="64dp"/>
<TextView
@@ -14,6 +15,7 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="2dp"
+ android:textIsSelectable="false"
android:textColor="@android:color/white"
android:textStyle="bold"/>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
index 299865be9e..1ef2c69012 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
@@ -11,6 +11,7 @@
android:layout_height="wrap_content"
android:textStyle="bold"
android:padding="4dp"
+ android:textIsSelectable="false"
android:layout_centerInParent="true" />
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml
index 92d1dd5380..ff65f319f9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml
@@ -3,10 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_action_intensity"
- android:title="Change intensity"
+ android:title="@string/change_intensity"
app:showAsAction="never"/>
<item
android:id="@+id/menu_action_anchor"
- android:title="Change Anchor"
+ android:title="@string/change_anchor"
app:showAsAction="never"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml
index 8b7245c5ca..43a191f7b1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/spinner"
- android:title="Amount of markers"
+ android:title="@string/amount_of_markers"
app:actionViewClass="android.widget.Spinner"
app:showAsAction="always"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml
index 4639dd65ba..915afd77fa 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml
@@ -1,17 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:mapbox="http://schemas.android.com/apk/res-auto">
-
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_update_layer"
- android:title="Update layer (invalidate)" />
+ android:title="@string/update_layer_invalidate"/>
<item
android:id="@+id/action_set_color_red"
- android:title="Red" />
+ android:title="@string/red"/>
<item
android:id="@+id/action_set_color_green"
- android:title="Green" />
+ android:title="@string/green"/>
<item
android:id="@+id/action_set_color_blue"
- android:title="Blue" />
+ android:title="@string/blue"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml
index 3eae56a273..a596ff5708 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml
@@ -4,47 +4,47 @@
<item
android:id="@+id/action_add_exponential_zoom_function"
- android:title="Add an exponential zoom function"
+ android:title="@string/add_an_exponential_zoom_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_interval_zoom_function"
- android:title="Add an interval zoom function"
+ android:title="@string/add_an_interval_zoom_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_categorical_source_function"
- android:title="Add a categorical source function"
+ android:title="@string/add_a_categorical_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_exponential_source_function"
- android:title="Add an exponential source function"
+ android:title="@string/add_an_exponential_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_identity_source_function"
- android:title="Add an identity source function"
+ android:title="@string/add_an_identity_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_interval_source_function"
- android:title="Add an interval source function"
+ android:title="@string/add_an_interval_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_composite_exponential_function"
- android:title="Add a composite, exponential function"
+ android:title="@string/add_a_composite_exponential_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_composite_categorical_function"
- android:title="Add a composite, categorical function"
+ android:title="@string/add_a_composite_categorical_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_composite_interval_function"
- android:title="Add a composite, interval function"
+ android:title="@string/add_a_composite_interval_function"
mapbox:showAsAction="never"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml
new file mode 100644
index 0000000000..168361a263
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/menu_action_icon_overlap"
+ android:title="@string/menuitem_change_icon_overlap"
+ app:showAsAction="never"/>
+</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
index e0052d4a8c..7132c0c2a9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
@@ -3,10 +3,10 @@
xmlns:mapbox="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_user_tracking"
- android:title="My Location Tracking"
+ android:title="@string/my_location_tracking"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_bangalore"
- android:title="Bangalore"
+ android:title="@string/bangalore"
mapbox:showAsAction="never" />
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
index 0b3a8e797e..7d20442c8c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
@@ -4,6 +4,5 @@
<item
android:id="@+id/menuItemReset"
android:title="@string/menuitem_title_reset"
- app:showAsAction="always"
- />
+ app:showAsAction="always"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
index 86f0b4faee..5c77179272 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
@@ -4,66 +4,66 @@
<item
android:id="@+id/action_list_layers"
- android:title="List all layers in the style"
+ android:title="@string/list_all_layers_in_the_style"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_list_sources"
- android:title="List all sources in the style"
+ android:title="@string/list_all_sources_in_the_style"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_water_color"
- android:title="Color the water"
+ android:title="@string/color_the_water"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_background_opacity"
- android:title="Set background opacity"
+ android:title="@string/set_background_opacity"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_road_avoid_edges"
- android:title="Set road symbol placement to Point"
+ android:title="@string/set_road_symbol_placement_to_point"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_layer_visibility"
- android:title="Set layer visibility to false"
+ android:title="@string/set_layer_visibility_to_false"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_parks_layer"
- android:title="Add a parks layer"
+ android:title="@string/add_a_parks_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_dynamic_parks_layer"
- android:title="Add a dynamic GeoJSON source"
+ android:title="@string/add_a_dynamic_geojson_source"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_remove_layer"
- android:title="Remove buildings layer"
+ android:title="@string/remove_buildings_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_terrain_layer"
- android:title="Add a terrain layer"
+ android:title="@string/add_a_terrain_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_satellite_layer"
- android:title="Add a satellite layer"
+ android:title="@string/add_a_satellite_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_update_water_color_on_zoom"
- android:title="Change the water color on zoom"
+ android:title="@string/change_the_water_color_on_zoom"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_custom_tiles"
- android:title="Custom tiles"
+ android:title="@string/custom_tiles"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_fill_filter"
- android:title="Apply filtered fill"
+ android:title="@string/apply_filtered_fill"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_line_filter"
- android:title="Apply filtered line"
+ android:title="@string/apply_filtered_line"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_numeric_filter"
- android:title="Apply numeric fill filter"
+ android:title="@string/apply_numeric_fill_filter"
mapbox:showAsAction="never" />
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml
index 77468b4861..8f396b07b0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml
@@ -3,14 +3,14 @@
<item
android:id="@+id/action_toggle_text_size"
- android:title="Toggle text size"/>
+ android:title="@string/toggle_text_size"/>
<item
android:id="@+id/action_toggle_text_field"
- android:title="Toggle text field contents"/>
+ android:title="@string/toggle_text_field_contents"/>
<item
android:id="@+id/action_toggle_text_font"
- android:title="Toggle text font"/>
+ android:title="@string/toggle_text_font"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml
new file mode 100644
index 0000000000..3e5c8ab14c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/menu_action_change_location"
+ android:title="@string/menuitem_change_location"
+ app:showAsAction="never"/>
+</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml
index 0cea519a24..67c0b2df55 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml
@@ -3,23 +3,23 @@
xmlns:mapbox="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_zoom_in"
- android:title="Zoom in"
+ android:title="@string/zoom_in"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_out"
- android:title="Zoom out"
+ android:title="@string/zoom_out"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_by"
- android:title="Zoom by 2"
+ android:title="@string/zoom_by_2"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_to_point"
- android:title="Zoom to point"
+ android:title="@string/zoom_to_point"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_to"
- android:title="Zoom to 4"
+ android:title="@string/zoom_to_4"
mapbox:showAsAction="never" />
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json
new file mode 100644
index 0000000000..f2cd969be4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json
@@ -0,0 +1,31 @@
+{
+ "version": 8,
+ "name": "Satellite",
+ "metadata": {
+ "mapbox:autocomposite": true
+ },
+ "sources": {
+ "mapbox": {
+ "type": "raster",
+ "url": "mapbox://mapbox.satellite",
+ "tileSize": 256
+ }
+ },
+ "sprite": "mapbox://sprites/mapbox/satellite-v8",
+ "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "rgb(4,7,14)"
+ }
+ },
+ {
+ "id": "satellite",
+ "type": "raster",
+ "source": "mapbox",
+ "source-layer": "mapbox_satellite_full"
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson
new file mode 100644
index 0000000000..caff2ac81c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson
@@ -0,0 +1,2741 @@
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 1,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Vanuatu",
+ "sov_a3": "VUT",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Vanuatu",
+ "adm0_a3": "VUT",
+ "geou_dif": 0,
+ "geounit": "Vanuatu",
+ "gu_a3": "VUT",
+ "su_dif": 0,
+ "subunit": "Vanuatu",
+ "su_a3": "VUT",
+ "brk_diff": 0,
+ "name": "Vanuatu",
+ "name_long": "Vanuatu",
+ "brk_a3": "VUT",
+ "brk_name": "Vanuatu",
+ "brk_group": null,
+ "abbrev": "Van.",
+ "postal": "VU",
+ "formal_en": "Republic of Vanuatu",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Vanuatu",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 3,
+ "mapcolor9": 7,
+ "mapcolor13": 3,
+ "pop_est": 218519,
+ "gdp_md_est": 988.5,
+ "pop_year": -99,
+ "lastcensus": 2009,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "VU",
+ "iso_a3": "VUT",
+ "iso_n3": "548",
+ "un_a3": "548",
+ "wb_a2": "VU",
+ "wb_a3": "VUT",
+ "woe_id": -99,
+ "adm0_a3_is": "VUT",
+ "adm0_a3_us": "VUT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Melanesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 166.9270664395989,
+ -15.367957152169708
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 1,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "France",
+ "sov_a3": "FR1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "French Southern and Antarctic Lands",
+ "adm0_a3": "ATF",
+ "geou_dif": 0,
+ "geounit": "French Southern and Antarctic Lands",
+ "gu_a3": "ATF",
+ "su_dif": 0,
+ "subunit": "French Southern and Antarctic Lands",
+ "su_a3": "ATF",
+ "brk_diff": 0,
+ "name": "Fr. S. Antarctic Lands",
+ "name_long": "French Southern and Antarctic Lands",
+ "brk_a3": "ATF",
+ "brk_name": "Fr. S. and Antarctic Lands",
+ "brk_group": null,
+ "abbrev": "Fr. S.A.L.",
+ "postal": "TF",
+ "formal_en": "Territory of the French Southern and Antarctic Lands",
+ "formal_fr": null,
+ "note_adm0": "Fr.",
+ "note_brk": null,
+ "name_sort": "French Southern and Antarctic Lands",
+ "name_alt": null,
+ "mapcolor7": 7,
+ "mapcolor8": 5,
+ "mapcolor9": 9,
+ "mapcolor13": 11,
+ "pop_est": 140,
+ "gdp_md_est": 16,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TF",
+ "iso_a3": "ATF",
+ "iso_n3": "260",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "ATF",
+ "adm0_a3_us": "ATF",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 22,
+ "long_len": 35,
+ "abbrev_len": 10,
+ "tiny": 2,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 69.22513999086925,
+ -49.33878196163545
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "New Zealand",
+ "sov_a3": "NZ1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Cook Islands",
+ "adm0_a3": "COK",
+ "geou_dif": 0,
+ "geounit": "Cook Islands",
+ "gu_a3": "COK",
+ "su_dif": 0,
+ "subunit": "Cook Islands",
+ "su_a3": "COK",
+ "brk_diff": 0,
+ "name": "Cook Is.",
+ "name_long": "Cook Islands",
+ "brk_a3": "COK",
+ "brk_name": "Cook Is.",
+ "brk_group": null,
+ "abbrev": "Cook Is.",
+ "postal": "CK",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Assoc. with N.Z.",
+ "note_brk": null,
+ "name_sort": "Cook Islands",
+ "name_alt": null,
+ "mapcolor7": 3,
+ "mapcolor8": 3,
+ "mapcolor9": 4,
+ "mapcolor13": 4,
+ "pop_est": 11870,
+ "gdp_md_est": 183.2,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "CK",
+ "iso_a3": "COK",
+ "iso_n3": "184",
+ "un_a3": "184",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "COK",
+ "adm0_a3_us": "COK",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 8,
+ "long_len": 12,
+ "abbrev_len": 8,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -159.78922694470387,
+ -21.220086945691605
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Samoa",
+ "sov_a3": "WSM",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Samoa",
+ "adm0_a3": "WSM",
+ "geou_dif": 0,
+ "geounit": "Samoa",
+ "gu_a3": "WSM",
+ "su_dif": 0,
+ "subunit": "Samoa",
+ "su_a3": "WSM",
+ "brk_diff": 0,
+ "name": "Samoa",
+ "name_long": "Samoa",
+ "brk_a3": "WSM",
+ "brk_name": "Samoa",
+ "brk_group": null,
+ "abbrev": "Samoa",
+ "postal": "WS",
+ "formal_en": "Independent State of Samoa",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Samoa",
+ "name_alt": null,
+ "mapcolor7": 3,
+ "mapcolor8": 3,
+ "mapcolor9": 4,
+ "mapcolor13": 6,
+ "pop_est": 219998,
+ "gdp_md_est": 1049,
+ "pop_year": -99,
+ "lastcensus": 2006,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "WS",
+ "iso_a3": "WSM",
+ "iso_n3": "882",
+ "un_a3": "882",
+ "wb_a2": "WS",
+ "wb_a3": "WSM",
+ "woe_id": -99,
+ "adm0_a3_is": "WSM",
+ "adm0_a3_us": "WSM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": -99,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -172.41373026688336,
+ -13.637369985140253
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 4,
+ "sovereignt": "Tonga",
+ "sov_a3": "TON",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Tonga",
+ "adm0_a3": "TON",
+ "geou_dif": 0,
+ "geounit": "Tonga",
+ "gu_a3": "TON",
+ "su_dif": 0,
+ "subunit": "Tonga",
+ "su_a3": "TON",
+ "brk_diff": 0,
+ "name": "Tonga",
+ "name_long": "Tonga",
+ "brk_a3": "TON",
+ "brk_name": "Tonga",
+ "brk_group": null,
+ "abbrev": "Tongo",
+ "postal": "TO",
+ "formal_en": "Kingdom of Tonga",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Tonga",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 1,
+ "mapcolor9": 1,
+ "mapcolor13": 8,
+ "pop_est": 120898,
+ "gdp_md_est": 549,
+ "pop_year": -99,
+ "lastcensus": 2006,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TO",
+ "iso_a3": "TON",
+ "iso_n3": "776",
+ "un_a3": "776",
+ "wb_a2": "TO",
+ "wb_a3": "TON",
+ "woe_id": -99,
+ "adm0_a3_is": "TON",
+ "adm0_a3_us": "TON",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -175.23533295466754,
+ -21.158187998515473
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 5,
+ "sovereignt": "France",
+ "sov_a3": "FR1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "French Polynesia",
+ "adm0_a3": "PYF",
+ "geou_dif": 0,
+ "geounit": "French Polynesia",
+ "gu_a3": "PYF",
+ "su_dif": 0,
+ "subunit": "French Polynesia",
+ "su_a3": "PYF",
+ "brk_diff": 0,
+ "name": "Fr. Polynesia",
+ "name_long": "French Polynesia",
+ "brk_a3": "PYF",
+ "brk_name": "Fr. Polynesia",
+ "brk_group": null,
+ "abbrev": "Fr. Poly.",
+ "postal": "PF",
+ "formal_en": "French Polynesia",
+ "formal_fr": null,
+ "note_adm0": "Fr.",
+ "note_brk": null,
+ "name_sort": "French Polynesia",
+ "name_alt": null,
+ "mapcolor7": 7,
+ "mapcolor8": 5,
+ "mapcolor9": 9,
+ "mapcolor13": 11,
+ "pop_est": 287032,
+ "gdp_md_est": 4718,
+ "pop_year": -99,
+ "lastcensus": 2007,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PF",
+ "iso_a3": "PYF",
+ "iso_n3": "258",
+ "un_a3": "258",
+ "wb_a2": "PF",
+ "wb_a3": "PYF",
+ "woe_id": -99,
+ "adm0_a3_is": "PYF",
+ "adm0_a3_us": "PYF",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 13,
+ "long_len": 16,
+ "abbrev_len": 9,
+ "tiny": 2,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -149.47549597877855,
+ -17.6250049835121
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Pitcairn Islands",
+ "adm0_a3": "PCN",
+ "geou_dif": 0,
+ "geounit": "Pitcairn Islands",
+ "gu_a3": "PCN",
+ "su_dif": 0,
+ "subunit": "Pitcairn Islands",
+ "su_a3": "PCN",
+ "brk_diff": 0,
+ "name": "Pitcairn Is.",
+ "name_long": "Pitcairn Islands",
+ "brk_a3": "PCN",
+ "brk_name": "Pitcairn Is.",
+ "brk_group": null,
+ "abbrev": "Pit. Is.",
+ "postal": "PN",
+ "formal_en": "Pitcairn, Henderson, Ducie and Oeno Islands",
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "Pitcairn Islands",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 48,
+ "gdp_md_est": 0.72,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PN",
+ "iso_a3": "PCN",
+ "iso_n3": "612",
+ "un_a3": "612",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "PCN",
+ "adm0_a3_us": "PCN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 12,
+ "long_len": 16,
+ "abbrev_len": 8,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -128.31780012096033,
+ -24.364139777771015
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 5,
+ "sovereignt": "Barbados",
+ "sov_a3": "BRB",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Barbados",
+ "adm0_a3": "BRB",
+ "geou_dif": 0,
+ "geounit": "Barbados",
+ "gu_a3": "BRB",
+ "su_dif": 0,
+ "subunit": "Barbados",
+ "su_a3": "BRB",
+ "brk_diff": 0,
+ "name": "Barbados",
+ "name_long": "Barbados",
+ "brk_a3": "BRB",
+ "brk_name": "Barbados",
+ "brk_group": null,
+ "abbrev": "Barb.",
+ "postal": "BB",
+ "formal_en": "Barbados",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Barbados",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 1,
+ "mapcolor9": 5,
+ "mapcolor13": 3,
+ "pop_est": 284589,
+ "gdp_md_est": 5425,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BB",
+ "iso_a3": "BRB",
+ "iso_n3": "052",
+ "un_a3": "052",
+ "wb_a2": "BB",
+ "wb_a3": "BRB",
+ "woe_id": -99,
+ "adm0_a3_is": "BRB",
+ "adm0_a3_us": "BRB",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Caribbean",
+ "region_wb": "Latin America & Caribbean",
+ "name_len": 8,
+ "long_len": 8,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -59.554305983838844,
+ 13.174672374462602
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 5,
+ "sovereignt": "Trinidad and Tobago",
+ "sov_a3": "TTO",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Trinidad and Tobago",
+ "adm0_a3": "TTO",
+ "geou_dif": 0,
+ "geounit": "Trinidad and Tobago",
+ "gu_a3": "TTO",
+ "su_dif": 0,
+ "subunit": "Trinidad and Tobago",
+ "su_a3": "TTO",
+ "brk_diff": 0,
+ "name": "Trinidad and Tobago",
+ "name_long": "Trinidad and Tobago",
+ "brk_a3": "TTO",
+ "brk_name": "Trinidad and Tobago",
+ "brk_group": null,
+ "abbrev": "Tr.T.",
+ "postal": "TT",
+ "formal_en": "Republic of Trinidad and Tobago",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Trinidad and Tobago",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 6,
+ "mapcolor9": 2,
+ "mapcolor13": 5,
+ "pop_est": 1310000,
+ "gdp_md_est": 29010,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TT",
+ "iso_a3": "TTO",
+ "iso_n3": "780",
+ "un_a3": "780",
+ "wb_a2": "TT",
+ "wb_a3": "TTO",
+ "woe_id": -99,
+ "adm0_a3_is": "TTO",
+ "adm0_a3_us": "TTO",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Caribbean",
+ "region_wb": "Latin America & Caribbean",
+ "name_len": 19,
+ "long_len": 19,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -61.255188941565905,
+ 10.43680324164859
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 4,
+ "sovereignt": "Sao Tome and Principe",
+ "sov_a3": "STP",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Sao Tome and Principe",
+ "adm0_a3": "STP",
+ "geou_dif": 0,
+ "geounit": "Sao Tome and Principe",
+ "gu_a3": "STP",
+ "su_dif": 0,
+ "subunit": "Sao Tome and Principe",
+ "su_a3": "STP",
+ "brk_diff": 0,
+ "name": "São Tomé and Principe",
+ "name_long": "São Tomé and Principe",
+ "brk_a3": "STP",
+ "brk_name": "Sao Tome and Principe",
+ "brk_group": null,
+ "abbrev": "S.T.P.",
+ "postal": "ST",
+ "formal_en": "Democratic Republic of São Tomé and Principe",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "São Tomé and Principe",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 6,
+ "mapcolor9": 1,
+ "mapcolor13": 7,
+ "pop_est": 212679,
+ "gdp_md_est": 276.5,
+ "pop_year": -99,
+ "lastcensus": 2001,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "ST",
+ "iso_a3": "STP",
+ "iso_n3": "678",
+ "un_a3": "678",
+ "wb_a2": "ST",
+ "wb_a3": "STP",
+ "woe_id": -99,
+ "adm0_a3_is": "STP",
+ "adm0_a3_us": "STP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Africa",
+ "subregion": "Middle Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 21,
+ "long_len": 21,
+ "abbrev_len": 6,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.617198520543866,
+ 0.246806952308191
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 4,
+ "type": "Geo subunit",
+ "admin": "Saint Helena",
+ "adm0_a3": "SHN",
+ "geou_dif": 0,
+ "geounit": "Saint Helena",
+ "gu_a3": "SHN",
+ "su_dif": 1,
+ "subunit": "Ascension",
+ "su_a3": "BAC",
+ "brk_diff": 0,
+ "name": "Ascension",
+ "name_long": "Ascension",
+ "brk_a3": "BAC",
+ "brk_name": "Ascension",
+ "brk_group": null,
+ "abbrev": "Asc.",
+ "postal": "AS",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "Ascension",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 940,
+ "gdp_md_est": 2.21553,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "-99",
+ "income_grp": "-99",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SHN",
+ "adm0_a3_us": "SHN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Antarctica",
+ "name_len": 9,
+ "long_len": 9,
+ "abbrev_len": 4,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -14.362068334482444,
+ -7.939246540570252
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Saint Helena",
+ "adm0_a3": "SHN",
+ "geou_dif": 0,
+ "geounit": "Saint Helena",
+ "gu_a3": "SHN",
+ "su_dif": 0,
+ "subunit": "Saint Helena",
+ "su_a3": "SHN",
+ "brk_diff": 0,
+ "name": "Saint Helena",
+ "name_long": "Saint Helena",
+ "brk_a3": "SHN",
+ "brk_name": "Saint Helena",
+ "brk_group": null,
+ "abbrev": "St.H.",
+ "postal": "SH",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "St. Helena",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 7637,
+ "gdp_md_est": 18,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "SH",
+ "iso_a3": "SHN",
+ "iso_n3": "654",
+ "un_a3": "654",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SHN",
+ "adm0_a3_us": "SHN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Africa",
+ "subregion": "Western Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 12,
+ "long_len": 12,
+ "abbrev_len": 5,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -5.716296101395358,
+ -15.963221612123107
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 5,
+ "sovereignt": "Malta",
+ "sov_a3": "MLT",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Malta",
+ "adm0_a3": "MLT",
+ "geou_dif": 0,
+ "geounit": "Malta",
+ "gu_a3": "MLT",
+ "su_dif": 0,
+ "subunit": "Malta",
+ "su_a3": "MLT",
+ "brk_diff": 0,
+ "name": "Malta",
+ "name_long": "Malta",
+ "brk_a3": "MLT",
+ "brk_name": "Malta",
+ "brk_group": null,
+ "abbrev": "Malta",
+ "postal": "M",
+ "formal_en": "Republic of Malta",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Malta",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 4,
+ "mapcolor9": 1,
+ "mapcolor13": 8,
+ "pop_est": 405165,
+ "gdp_md_est": 9962,
+ "pop_year": -99,
+ "lastcensus": 2005,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MT",
+ "iso_a3": "MLT",
+ "iso_n3": "470",
+ "un_a3": "470",
+ "wb_a2": "MT",
+ "wb_a3": "MLT",
+ "woe_id": -99,
+ "adm0_a3_is": "MLT",
+ "adm0_a3_us": "MLT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Europe",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Middle East & North Africa",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 14.438179478988388,
+ 35.882081031796645
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 2,
+ "sovereignt": "Bahrain",
+ "sov_a3": "BHR",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Bahrain",
+ "adm0_a3": "BHR",
+ "geou_dif": 0,
+ "geounit": "Bahrain",
+ "gu_a3": "BHR",
+ "su_dif": 0,
+ "subunit": "Bahrain",
+ "su_a3": "BHR",
+ "brk_diff": 0,
+ "name": "Bahrain",
+ "name_long": "Bahrain",
+ "brk_a3": "BHR",
+ "brk_name": "Bahrain",
+ "brk_group": null,
+ "abbrev": "Bahr.",
+ "postal": "BH",
+ "formal_en": "Kingdom of Bahrain",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Bahrain",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 1,
+ "mapcolor9": 1,
+ "mapcolor13": 9,
+ "pop_est": 727785,
+ "gdp_md_est": 26820,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BH",
+ "iso_a3": "BHR",
+ "iso_n3": "048",
+ "un_a3": "048",
+ "wb_a2": "BH",
+ "wb_a3": "BHR",
+ "woe_id": -99,
+ "adm0_a3_is": "BHR",
+ "adm0_a3_us": "BHR",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Asia",
+ "region_un": "Asia",
+ "subregion": "Western Asia",
+ "region_wb": "Middle East & North Africa",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 50.553638136605,
+ 26.06944265390905
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Maldives",
+ "sov_a3": "MDV",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Maldives",
+ "adm0_a3": "MDV",
+ "geou_dif": 0,
+ "geounit": "Maldives",
+ "gu_a3": "MDV",
+ "su_dif": 0,
+ "subunit": "Maldives",
+ "su_a3": "MDV",
+ "brk_diff": 0,
+ "name": "Maldives",
+ "name_long": "Maldives",
+ "brk_a3": "MDV",
+ "brk_name": "Maldives",
+ "brk_group": null,
+ "abbrev": "Mald.",
+ "postal": "MV",
+ "formal_en": "Republic of Maldives",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Maldives",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 3,
+ "mapcolor9": 1,
+ "mapcolor13": 7,
+ "pop_est": 396334,
+ "gdp_md_est": 1716,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MV",
+ "iso_a3": "MDV",
+ "iso_n3": "462",
+ "un_a3": "462",
+ "wb_a2": "MV",
+ "wb_a3": "MDV",
+ "woe_id": -99,
+ "adm0_a3_is": "MDV",
+ "adm0_a3_us": "B13",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Asia",
+ "subregion": "Southern Asia",
+ "region_wb": "South Asia",
+ "name_len": 8,
+ "long_len": 8,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 73.50223056083513,
+ 4.186658727806048
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "British Indian Ocean Territory",
+ "adm0_a3": "IOT",
+ "geou_dif": 0,
+ "geounit": "British Indian Ocean Territory",
+ "gu_a3": "IOT",
+ "su_dif": 0,
+ "subunit": "British Indian Ocean Territory",
+ "su_a3": "IOT",
+ "brk_diff": 1,
+ "name": "Br. Indian Ocean Ter.",
+ "name_long": "British Indian Ocean Territory",
+ "brk_a3": "B69",
+ "brk_name": "Br. Indian Ocean Ter.",
+ "brk_group": null,
+ "abbrev": "I.O.T.",
+ "postal": "IO",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": "Admin. by U.K.; Claimed by Mauritius and Seychelles",
+ "name_sort": "British Indian Ocean Territory",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 4000,
+ "gdp_md_est": 160,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "IO",
+ "iso_a3": "IOT",
+ "iso_n3": "086",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "IOT",
+ "adm0_a3_us": "IOT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 21,
+ "long_len": 30,
+ "abbrev_len": 6,
+ "tiny": 5,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 72.47872949418257,
+ -7.340705873210993
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 3,
+ "sovereignt": "Singapore",
+ "sov_a3": "SGP",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Singapore",
+ "adm0_a3": "SGP",
+ "geou_dif": 0,
+ "geounit": "Singapore",
+ "gu_a3": "SGP",
+ "su_dif": 0,
+ "subunit": "Singapore",
+ "su_a3": "SGP",
+ "brk_diff": 0,
+ "name": "Singapore",
+ "name_long": "Singapore",
+ "brk_a3": "SGP",
+ "brk_name": "Singapore",
+ "brk_group": null,
+ "abbrev": "Sing.",
+ "postal": "SG",
+ "formal_en": "Republic of Singapore",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Singapore",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 3,
+ "mapcolor9": 7,
+ "mapcolor13": 3,
+ "pop_est": 4657542,
+ "gdp_md_est": 237300,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "SG",
+ "iso_a3": "SGP",
+ "iso_n3": "702",
+ "un_a3": "702",
+ "wb_a2": "SG",
+ "wb_a3": "SGP",
+ "woe_id": -99,
+ "adm0_a3_is": "SGP",
+ "adm0_a3_us": "SGP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Asia",
+ "region_un": "Asia",
+ "subregion": "South-Eastern Asia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 9,
+ "long_len": 9,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 103.81481982900323,
+ 1.359363931813562
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 2,
+ "sovereignt": "Brunei",
+ "sov_a3": "BRN",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Brunei",
+ "adm0_a3": "BRN",
+ "geou_dif": 0,
+ "geounit": "Brunei",
+ "gu_a3": "BRN",
+ "su_dif": 0,
+ "subunit": "Brunei",
+ "su_a3": "BRN",
+ "brk_diff": 0,
+ "name": "Brunei",
+ "name_long": "Brunei Darussalam",
+ "brk_a3": "BRN",
+ "brk_name": "Brunei",
+ "brk_group": null,
+ "abbrev": "Brunei",
+ "postal": "BN",
+ "formal_en": "Negara Brunei Darussalam",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Brunei",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 12,
+ "pop_est": 388190,
+ "gdp_md_est": 20250,
+ "pop_year": -99,
+ "lastcensus": 2001,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BN",
+ "iso_a3": "BRN",
+ "iso_n3": "096",
+ "un_a3": "096",
+ "wb_a2": "BN",
+ "wb_a3": "BRN",
+ "woe_id": -99,
+ "adm0_a3_is": "BRN",
+ "adm0_a3_us": "BRN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Asia",
+ "region_un": "Asia",
+ "subregion": "South-Eastern Asia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 6,
+ "long_len": 17,
+ "abbrev_len": 6,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 114.56745460338925,
+ 4.434669496170784
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Palau",
+ "sov_a3": "PLW",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Palau",
+ "adm0_a3": "PLW",
+ "geou_dif": 0,
+ "geounit": "Palau",
+ "gu_a3": "PLW",
+ "su_dif": 0,
+ "subunit": "Palau",
+ "su_a3": "PLW",
+ "brk_diff": 0,
+ "name": "Palau",
+ "name_long": "Palau",
+ "brk_a3": "PLW",
+ "brk_name": "Palau",
+ "brk_group": null,
+ "abbrev": "Palau",
+ "postal": "PW",
+ "formal_en": "Republic of Palau",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Palau",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 5,
+ "mapcolor9": 1,
+ "mapcolor13": 12,
+ "pop_est": 20796,
+ "gdp_md_est": 164,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PW",
+ "iso_a3": "PLW",
+ "iso_n3": "585",
+ "un_a3": "585",
+ "wb_a2": "PW",
+ "wb_a3": "PLW",
+ "woe_id": -99,
+ "adm0_a3_is": "PLW",
+ "adm0_a3_us": "PLW",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 134.57924133620793,
+ 7.507494163314107
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United States of America",
+ "sov_a3": "US1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Northern Mariana Islands",
+ "adm0_a3": "MNP",
+ "geou_dif": 0,
+ "geounit": "Northern Mariana Islands",
+ "gu_a3": "MNP",
+ "su_dif": 0,
+ "subunit": "Northern Mariana Islands",
+ "su_a3": "MNP",
+ "brk_diff": 0,
+ "name": "N. Mariana Is.",
+ "name_long": "Northern Mariana Islands",
+ "brk_a3": "MNP",
+ "brk_name": "N. Mariana Is.",
+ "brk_group": null,
+ "abbrev": "N.M.I.",
+ "postal": "MP",
+ "formal_en": "Commonwealth of the Northern Mariana Islands",
+ "formal_fr": null,
+ "note_adm0": "Commonwealth of U.S.A.",
+ "note_brk": null,
+ "name_sort": "Northern Mariana Islands",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 5,
+ "mapcolor9": 1,
+ "mapcolor13": 1,
+ "pop_est": 88662,
+ "gdp_md_est": 900,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MP",
+ "iso_a3": "MNP",
+ "iso_n3": "580",
+ "un_a3": "580",
+ "wb_a2": "MP",
+ "wb_a3": "MNP",
+ "woe_id": -99,
+ "adm0_a3_is": "MNP",
+ "adm0_a3_us": "MNP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 14,
+ "long_len": 24,
+ "abbrev_len": 6,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 145.73926332724704,
+ 15.17463695328189
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "United States of America",
+ "sov_a3": "US1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Guam",
+ "adm0_a3": "GUM",
+ "geou_dif": 0,
+ "geounit": "Guam",
+ "gu_a3": "GUM",
+ "su_dif": 0,
+ "subunit": "Guam",
+ "su_a3": "GUM",
+ "brk_diff": 0,
+ "name": "Guam",
+ "name_long": "Guam",
+ "brk_a3": "GUM",
+ "brk_name": "Guam",
+ "brk_group": null,
+ "abbrev": "Guam",
+ "postal": "GU",
+ "formal_en": "Territory of Guam",
+ "formal_fr": null,
+ "note_adm0": "U.S.A.",
+ "note_brk": null,
+ "name_sort": "Guam",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 5,
+ "mapcolor9": 1,
+ "mapcolor13": 1,
+ "pop_est": 178430,
+ "gdp_md_est": 2500,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "GU",
+ "iso_a3": "GUM",
+ "iso_n3": "316",
+ "un_a3": "316",
+ "wb_a2": "GU",
+ "wb_a3": "GUM",
+ "woe_id": -99,
+ "adm0_a3_is": "GUM",
+ "adm0_a3_us": "GUM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 4,
+ "long_len": 4,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 144.77003842181864,
+ 13.459684857600507
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Federated States of Micronesia",
+ "sov_a3": "FSM",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Federated States of Micronesia",
+ "adm0_a3": "FSM",
+ "geou_dif": 0,
+ "geounit": "Federated States of Micronesia",
+ "gu_a3": "FSM",
+ "su_dif": 0,
+ "subunit": "Federated States of Micronesia",
+ "su_a3": "FSM",
+ "brk_diff": 0,
+ "name": "Micronesia",
+ "name_long": "Federated States of Micronesia",
+ "brk_a3": "FSM",
+ "brk_name": "Micronesia",
+ "brk_group": null,
+ "abbrev": "F.S.M.",
+ "postal": "FSM",
+ "formal_en": "Federated States of Micronesia",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Micronesia, Federated States of",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 2,
+ "mapcolor9": 4,
+ "mapcolor13": 13,
+ "pop_est": 107434,
+ "gdp_md_est": 238.1,
+ "pop_year": -99,
+ "lastcensus": 2000,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "FM",
+ "iso_a3": "FSM",
+ "iso_n3": "583",
+ "un_a3": "583",
+ "wb_a2": "FM",
+ "wb_a3": "FSM",
+ "woe_id": -99,
+ "adm0_a3_is": "FSM",
+ "adm0_a3_us": "FSM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 10,
+ "long_len": 30,
+ "abbrev_len": 6,
+ "tiny": -99,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 158.2420151934607,
+ 6.885941535379288
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Marshall Islands",
+ "sov_a3": "MHL",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Marshall Islands",
+ "adm0_a3": "MHL",
+ "geou_dif": 0,
+ "geounit": "Marshall Islands",
+ "gu_a3": "MHL",
+ "su_dif": 0,
+ "subunit": "Marshall Islands",
+ "su_a3": "MHL",
+ "brk_diff": 0,
+ "name": "Marshall Is.",
+ "name_long": "Marshall Islands",
+ "brk_a3": "MHL",
+ "brk_name": "Marshall Is.",
+ "brk_group": null,
+ "abbrev": "M. Is.",
+ "postal": "MH",
+ "formal_en": "Republic of the Marshall Islands",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Marshall Islands",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 5,
+ "mapcolor9": 5,
+ "mapcolor13": 3,
+ "pop_est": 64522,
+ "gdp_md_est": 133.5,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MH",
+ "iso_a3": "MHL",
+ "iso_n3": "584",
+ "un_a3": "584",
+ "wb_a2": "MH",
+ "wb_a3": "MHL",
+ "woe_id": -99,
+ "adm0_a3_is": "MHL",
+ "adm0_a3_us": "MHL",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 12,
+ "long_len": 16,
+ "abbrev_len": 6,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 168.72896600641184,
+ 7.313460144816133
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Kiribati",
+ "sov_a3": "KIR",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Kiribati",
+ "adm0_a3": "KIR",
+ "geou_dif": 0,
+ "geounit": "Kiribati",
+ "gu_a3": "KIR",
+ "su_dif": 0,
+ "subunit": "Kiribati",
+ "su_a3": "KIR",
+ "brk_diff": 0,
+ "name": "Kiribati",
+ "name_long": "Kiribati",
+ "brk_a3": "KIR",
+ "brk_name": "Kiribati",
+ "brk_group": null,
+ "abbrev": "Kir.",
+ "postal": "KI",
+ "formal_en": "Republic of Kiribati",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Kiribati",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 7,
+ "mapcolor9": 6,
+ "mapcolor13": 12,
+ "pop_est": 112850,
+ "gdp_md_est": 579.5,
+ "pop_year": -99,
+ "lastcensus": 2005,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "KI",
+ "iso_a3": "KIR",
+ "iso_n3": "296",
+ "un_a3": "296",
+ "wb_a2": "KI",
+ "wb_a3": "KIR",
+ "woe_id": -99,
+ "adm0_a3_is": "KIR",
+ "adm0_a3_us": "KIR",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 8,
+ "long_len": 8,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 173.13515838316619,
+ 1.364258124187756
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Nauru",
+ "sov_a3": "NRU",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Nauru",
+ "adm0_a3": "NRU",
+ "geou_dif": 0,
+ "geounit": "Nauru",
+ "gu_a3": "NRU",
+ "su_dif": 0,
+ "subunit": "Nauru",
+ "su_a3": "NRU",
+ "brk_diff": 0,
+ "name": "Nauru",
+ "name_long": "Nauru",
+ "brk_a3": "NRU",
+ "brk_name": "Nauru",
+ "brk_group": null,
+ "abbrev": "Nauru",
+ "postal": "NR",
+ "formal_en": "Republic of Nauru",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Nauru",
+ "name_alt": null,
+ "mapcolor7": 3,
+ "mapcolor8": 7,
+ "mapcolor9": 6,
+ "mapcolor13": 9,
+ "pop_est": 14019,
+ "gdp_md_est": 60,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "NR",
+ "iso_a3": "NRU",
+ "iso_n3": "520",
+ "un_a3": "520",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "NRU",
+ "adm0_a3_us": "NRU",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 166.93748256244703,
+ -0.523068535976108
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 5,
+ "sr_label_o": 6,
+ "sovereignt": "Tuvalu",
+ "sov_a3": "TUV",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Tuvalu",
+ "adm0_a3": "TUV",
+ "geou_dif": 0,
+ "geounit": "Tuvalu",
+ "gu_a3": "TUV",
+ "su_dif": 0,
+ "subunit": "Tuvalu",
+ "su_a3": "TUV",
+ "brk_diff": 0,
+ "name": "Tuvalu",
+ "name_long": "Tuvalu",
+ "brk_a3": "TUV",
+ "brk_name": "Tuvalu",
+ "brk_group": null,
+ "abbrev": "Tuv.",
+ "postal": "TV",
+ "formal_en": "Tuvalu",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Tuvalu",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 3,
+ "mapcolor9": 8,
+ "mapcolor13": 5,
+ "pop_est": 12373,
+ "gdp_md_est": 14.94,
+ "pop_year": -99,
+ "lastcensus": 2002,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TV",
+ "iso_a3": "TUV",
+ "iso_n3": "798",
+ "un_a3": "798",
+ "wb_a2": "TV",
+ "wb_a3": "TUV",
+ "woe_id": -99,
+ "adm0_a3_is": "TUV",
+ "adm0_a3_us": "TUV",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 6,
+ "long_len": 6,
+ "abbrev_len": 4,
+ "tiny": 5,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 179.20397422623353,
+ -8.49972371316585
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Mauritius",
+ "sov_a3": "MUS",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Mauritius",
+ "adm0_a3": "MUS",
+ "geou_dif": 0,
+ "geounit": "Mauritius",
+ "gu_a3": "MUS",
+ "su_dif": 0,
+ "subunit": "Mauritius",
+ "su_a3": "MUS",
+ "brk_diff": 0,
+ "name": "Mauritius",
+ "name_long": "Mauritius",
+ "brk_a3": "MUS",
+ "brk_name": "Mauritius",
+ "brk_group": null,
+ "abbrev": "Mus.",
+ "postal": "MU",
+ "formal_en": "Republic of Mauritius",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Mauritius",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 3,
+ "mapcolor9": 5,
+ "mapcolor13": 7,
+ "pop_est": 1284264,
+ "gdp_md_est": 15270,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MU",
+ "iso_a3": "MUS",
+ "iso_n3": "480",
+ "un_a3": "480",
+ "wb_a2": "MU",
+ "wb_a3": "MUS",
+ "woe_id": -99,
+ "adm0_a3_is": "MUS",
+ "adm0_a3_us": "MUS",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Africa",
+ "subregion": "Eastern Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 9,
+ "long_len": 9,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 57.58565995816849,
+ -20.302274672122962
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Comoros",
+ "sov_a3": "COM",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Comoros",
+ "adm0_a3": "COM",
+ "geou_dif": 0,
+ "geounit": "Comoros",
+ "gu_a3": "COM",
+ "su_dif": 0,
+ "subunit": "Comoros",
+ "su_a3": "COM",
+ "brk_diff": 0,
+ "name": "Comoros",
+ "name_long": "Comoros",
+ "brk_a3": "COM",
+ "brk_name": "Comoros",
+ "brk_group": null,
+ "abbrev": "Com.",
+ "postal": "KM",
+ "formal_en": "Union of the Comoros",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Comoros",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 1,
+ "mapcolor9": 4,
+ "mapcolor13": 10,
+ "pop_est": 752438,
+ "gdp_md_est": 751.2,
+ "pop_year": -99,
+ "lastcensus": 2003,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "KM",
+ "iso_a3": "COM",
+ "iso_n3": "174",
+ "un_a3": "174",
+ "wb_a2": "KM",
+ "wb_a3": "COM",
+ "woe_id": -99,
+ "adm0_a3_is": "COM",
+ "adm0_a3_us": "COM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Africa",
+ "subregion": "Eastern Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 43.337943198143535,
+ -11.715555516231973
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Denmark",
+ "sov_a3": "DN1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Faroe Islands",
+ "adm0_a3": "FRO",
+ "geou_dif": 0,
+ "geounit": "Faroe Islands",
+ "gu_a3": "FRO",
+ "su_dif": 0,
+ "subunit": "Faroe Islands",
+ "su_a3": "FRO",
+ "brk_diff": 0,
+ "name": "Faeroe Is.",
+ "name_long": "Faeroe Islands",
+ "brk_a3": "FRO",
+ "brk_name": "Faeroe Islands",
+ "brk_group": null,
+ "abbrev": "Faeroe Is.",
+ "postal": "FO",
+ "formal_en": "Føroyar Is. (Faeroe Is.)",
+ "formal_fr": null,
+ "note_adm0": "Den.",
+ "note_brk": null,
+ "name_sort": "Faeroe Islands",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 1,
+ "mapcolor9": 3,
+ "mapcolor13": 12,
+ "pop_est": 48856,
+ "gdp_md_est": 1000,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "FO",
+ "iso_a3": "FRO",
+ "iso_n3": "234",
+ "un_a3": "234",
+ "wb_a2": "FO",
+ "wb_a3": "FRO",
+ "woe_id": -99,
+ "adm0_a3_is": "FRO",
+ "adm0_a3_us": "FRO",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Europe",
+ "region_un": "Europe",
+ "subregion": "Northern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 10,
+ "long_len": 14,
+ "abbrev_len": 10,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -6.942567803221323,
+ 62.19161776035833
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Norway",
+ "sov_a3": "NOR",
+ "adm0_dif": 0,
+ "level": 3,
+ "type": "Geo unit",
+ "admin": "Norway",
+ "adm0_a3": "NOR",
+ "geou_dif": 1,
+ "geounit": "Jan Mayen",
+ "gu_a3": "NJM",
+ "su_dif": 0,
+ "subunit": "Jan Mayen",
+ "su_a3": "NJM",
+ "brk_diff": 0,
+ "name": "Jan Mayen I.",
+ "name_long": "Jan Mayen Island",
+ "brk_a3": "NJM",
+ "brk_name": "Jan Mayen",
+ "brk_group": null,
+ "abbrev": "J.M.",
+ "postal": "JM",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Nor.",
+ "note_brk": null,
+ "name_sort": "Jan Mayen I.",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 3,
+ "mapcolor9": 8,
+ "mapcolor13": 12,
+ "pop_est": 20,
+ "gdp_md_est": -99,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SJM",
+ "adm0_a3_us": "NOR",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Europe",
+ "region_un": "Europe",
+ "subregion": "Northern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 12,
+ "long_len": 16,
+ "abbrev_len": 4,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoUnit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -8.420617438175157,
+ 71.02824880643254
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "France",
+ "sov_a3": "FR1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Saint Pierre and Miquelon",
+ "adm0_a3": "SPM",
+ "geou_dif": 0,
+ "geounit": "Saint Pierre and Miquelon",
+ "gu_a3": "SPM",
+ "su_dif": 0,
+ "subunit": "Saint Pierre and Miquelon",
+ "su_a3": "SPM",
+ "brk_diff": 0,
+ "name": "St. Pierre and Miquelon",
+ "name_long": "Saint Pierre and Miquelon",
+ "brk_a3": "SPM",
+ "brk_name": "St. Pierre and Miquelon",
+ "brk_group": null,
+ "abbrev": "St. P.M.",
+ "postal": "PM",
+ "formal_en": "Saint Pierre and Miquelon",
+ "formal_fr": null,
+ "note_adm0": "Fr.",
+ "note_brk": null,
+ "name_sort": "St. Pierre and Miquelon",
+ "name_alt": null,
+ "mapcolor7": 7,
+ "mapcolor8": 5,
+ "mapcolor9": 9,
+ "mapcolor13": 11,
+ "pop_est": 7051,
+ "gdp_md_est": 48.3,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PM",
+ "iso_a3": "SPM",
+ "iso_n3": "666",
+ "un_a3": "666",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SPM",
+ "adm0_a3_us": "SPM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Northern America",
+ "region_wb": "North America",
+ "name_len": 23,
+ "long_len": 25,
+ "abbrev_len": 8,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -56.31570304234327,
+ 46.85746558614022
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Bermuda",
+ "adm0_a3": "BMU",
+ "geou_dif": 0,
+ "geounit": "Bermuda",
+ "gu_a3": "BMU",
+ "su_dif": 0,
+ "subunit": "Bermuda",
+ "su_a3": "BMU",
+ "brk_diff": 0,
+ "name": "Bermuda",
+ "name_long": "Bermuda",
+ "brk_a3": "BMU",
+ "brk_name": "Bermuda",
+ "brk_group": null,
+ "abbrev": "Berm.",
+ "postal": "BM",
+ "formal_en": "The Bermudas or Somers Isles",
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "Bermuda",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 67837,
+ "gdp_md_est": 4500,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BM",
+ "iso_a3": "BMU",
+ "iso_n3": "060",
+ "un_a3": "060",
+ "wb_a2": "BM",
+ "wb_a3": "BMU",
+ "woe_id": -99,
+ "adm0_a3_is": "BMU",
+ "adm0_a3_us": "BMU",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Northern America",
+ "region_wb": "North America",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 5,
+ "tiny": 4,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -64.74797798630703,
+ 32.307221641280876
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Portugal",
+ "sov_a3": "PRT",
+ "adm0_dif": 0,
+ "level": 3,
+ "type": "Geo unit",
+ "admin": "Portugal",
+ "adm0_a3": "PRT",
+ "geou_dif": 1,
+ "geounit": "Azores",
+ "gu_a3": "PAZ",
+ "su_dif": 0,
+ "subunit": "Azores",
+ "su_a3": "PAZ",
+ "brk_diff": 0,
+ "name": "Azores",
+ "name_long": "Azores",
+ "brk_a3": "PAZ",
+ "brk_name": "Azores",
+ "brk_group": null,
+ "abbrev": "Az.",
+ "postal": "AZ",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Port.",
+ "note_brk": null,
+ "name_sort": "Azores",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 7,
+ "mapcolor9": 1,
+ "mapcolor13": 4,
+ "pop_est": 235374,
+ "gdp_md_est": 4492,
+ "pop_year": 0,
+ "lastcensus": -99,
+ "gdp_year": 0,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "1. High income: OECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "-99",
+ "adm0_a3_us": "PRT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 6,
+ "long_len": 6,
+ "abbrev_len": 3,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoUnit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -28.423474244011175,
+ 38.48233011770992
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Spain",
+ "sov_a3": "ESP",
+ "adm0_dif": 0,
+ "level": 4,
+ "type": "Geo subunit",
+ "admin": "Spain",
+ "adm0_a3": "ESP",
+ "geou_dif": 0,
+ "geounit": "Spain",
+ "gu_a3": "ESP",
+ "su_dif": 1,
+ "subunit": "Canary Islands",
+ "su_a3": "ESC",
+ "brk_diff": 0,
+ "name": "Canary Is.",
+ "name_long": "Canary Islands",
+ "brk_a3": "ESC",
+ "brk_name": "Canary Is.",
+ "brk_group": null,
+ "abbrev": "Can. Is.",
+ "postal": "CI",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Sp.",
+ "note_brk": null,
+ "name_sort": "Canary Islands",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 5,
+ "mapcolor9": 5,
+ "mapcolor13": 5,
+ "pop_est": 2098593,
+ "gdp_md_est": 72654.55481,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "-99",
+ "income_grp": "-99",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "ESP",
+ "adm0_a3_us": "ESP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 10,
+ "long_len": 14,
+ "abbrev_len": 8,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoSubunit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -16.592772263568634,
+ 28.228989968662177
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Portugal",
+ "sov_a3": "PRT",
+ "adm0_dif": 0,
+ "level": 3,
+ "type": "Geo unit",
+ "admin": "Portugal",
+ "adm0_a3": "PRT",
+ "geou_dif": 1,
+ "geounit": "Madeira",
+ "gu_a3": "PMD",
+ "su_dif": 0,
+ "subunit": "Madeira",
+ "su_a3": "PMD",
+ "brk_diff": 0,
+ "name": "Madeira",
+ "name_long": "Madeira",
+ "brk_a3": "PMD",
+ "brk_name": "Madeira",
+ "brk_group": null,
+ "abbrev": "Mad.",
+ "postal": "MD",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Port.",
+ "note_brk": null,
+ "name_sort": "Madeira",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 7,
+ "mapcolor9": 1,
+ "mapcolor13": 4,
+ "pop_est": 267785,
+ "gdp_md_est": 6414,
+ "pop_year": 0,
+ "lastcensus": -99,
+ "gdp_year": 0,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "1. High income: OECD",
+ "wikipedia": 0,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "-99",
+ "adm0_a3_us": "PRT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 4,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoUnit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -16.959751345358598,
+ 32.74536514049669
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "South Georgia and South Sandwich Islands",
+ "adm0_a3": "SGS",
+ "geou_dif": 0,
+ "geounit": "South Georgia and South Sandwich Islands",
+ "gu_a3": "SGS",
+ "su_dif": 0,
+ "subunit": "South Georgia and South Sandwich Islands",
+ "su_a3": "SGS",
+ "brk_diff": 0,
+ "name": "S. Geo. and S. Sandw. Is.",
+ "name_long": "South Georgia and South Sandwich Islands",
+ "brk_a3": "SGS",
+ "brk_name": "S. Geo. and S. Sandw. Is.",
+ "brk_group": null,
+ "abbrev": "S.G. S.S. Is.",
+ "postal": "GS",
+ "formal_en": "South Georgia and South Sandwich Islands",
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "South Georgia and the Islands",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 30,
+ "gdp_md_est": 0.3,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "GS",
+ "iso_a3": "SGS",
+ "iso_n3": "239",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SGS",
+ "adm0_a3_us": "SGS",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Antarctica",
+ "name_len": 25,
+ "long_len": 40,
+ "abbrev_len": 13,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -36.792143407672654,
+ -54.274478863695265
+ ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
new file mode 100644
index 0000000000..4ca19def71
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>
+ <string name="menuitem_title_deselect_markers_on_tap">Deselect Markers On Tap</string>
+ <string name="menuitem_title_tracking_mode_dismiss_on_gesture">Dismiss location tracking on gesture</string>
+ <string name="menuitem_title_bearing_mode_dismiss_on_gesture">Dismiss bearing tracking on gesture</string>
+ <string name="menuitem_title_reset">Reset</string>
+ <string name="menuitem_title_rotate_gesture_enabled">Enable rotate gestures</string>
+ <string name="menuitem_title_scroll_gesture_enabled">Enable scroll gestures</string>
+ <string name="menuitem_title_change_location_source_lost">Change to LOST location source</string>
+ <string name="menuitem_title_change_location_source_mock">Change to mock location source</string>
+ <string name="menuitem_title_change_location_source_null">Reset location source to null</string>
+ <string name="menuitem_change_icon_overlap">Toggle icon overlap</string>
+ <string name="menuitem_change_location">Change location</string>
+ <string name="button_camera_move">Move</string>
+ <string name="button_camera_ease">Ease</string>
+ <string name="button_camera_animate">Animate</string>
+ <string name="button_user_dot_default">Default</string>
+ <string name="button_user_dot_tint">Tint dot</string>
+ <string name="button_user_accuracy_ring_tint">Tint ring</string>
+ <string name="button_user_transparent_tint">tran</string>
+ <string name="button_open_dialog">Open dialog</string>
+ <string name="button_download_region">Download region</string>
+ <string name="button_list_regions">List regions</string>
+ <string name="action_remove_polylines">Remove polylines</string>
+ <string name="action_visibility_polygon">Change visibility</string>
+ <string name="action_alpha_polygon">Change alpha</string>
+ <string name="action_points_polygon">Change points</string>
+ <string name="action_color_polygon">Change color</string>
+ <string name="action_holes_polygon">Change holes</string>
+ <string name="action_width_polyline">Change width</string>
+ <string name="action_calculate_distance">"Click the map to calculate the distance"</string>
+ <string name="action_scroll_by">Move the map by x/y pixels</string>
+ <string name="navigation_drawer_open">Open navigation drawer</string>
+ <string name="navigation_drawer_close">Close navigation drawer</string>
+ <string name="scrollby_x_value">X: %1$d</string>
+ <string name="scrollby_y_value">Y: %1$d</string>
+ <string name="dialog_camera_position">Animate to new position</string>
+ <string name="dynamic_marker_chelsea_title">Chelsea</string>
+ <string name="dynamic_marker_chelsea_snippet">Stamford Bridge</string>
+ <string name="dynamic_marker_arsenal_title">Arsenal</string>
+ <string name="dynamic_marker_arsenal_snippet">Emirates Stadium</string>
+ <string name="debug_zoom">Zoom: %.2f</string>
+ <string name="viewcache_size">ViewCache size %.2f</string>
+ <string name="latitude">Latitude</string>
+ <string name="min_value">-180</string>
+ <string name="longitude">Longitude</string>
+ <string name="zoom">Zoom</string>
+ <string name="default_zoom_value">18</string>
+ <string name="bearing">Bearing</string>
+ <string name="default_tilt_value">0</string>
+ <string name="tilt">Tilt</string>
+ <string name="no_results">No Results</string>
+ <string name="change_intensity">Change intensity</string>
+ <string name="change_anchor">Change Anchor</string>
+ <string name="amount_of_markers">Amount of markers</string>
+ <string name="update_layer_invalidate">Update layer (invalidate)</string>
+ <string name="red">Red</string>
+ <string name="green">Green</string>
+ <string name="blue">Blue</string>
+ <string name="add_an_exponential_zoom_function">Add an exponential zoom function</string>
+ <string name="add_an_interval_zoom_function">Add an interval zoom function</string>
+ <string name="add_a_categorical_source_function">Add a categorical source function</string>
+ <string name="add_an_exponential_source_function">Add an exponential source function</string>
+ <string name="add_an_identity_source_function">Add an identity source function</string>
+ <string name="add_an_interval_source_function">Add an interval source function</string>
+ <string name="add_a_composite_categorical_function">Add a composite, categorical function</string>
+ <string name="add_a_composite_exponential_function">Add a composite, exponential function</string>
+ <string name="add_a_composite_interval_function">Add a composite, interval function</string>
+ <string name="my_location_tracking">My Location Tracking</string>
+ <string name="bangalore">Bangalore</string>
+ <string name="list_all_layers_in_the_style">List all layers in the style</string>
+ <string name="list_all_sources_in_the_style">List all sources in the style</string>
+ <string name="color_the_water">Color the water</string>
+ <string name="set_background_opacity">Set background opacity</string>
+ <string name="set_road_symbol_placement_to_point">Set road symbol placement to Point</string>
+ <string name="set_layer_visibility_to_false">Set layer visibility to false</string>
+ <string name="add_a_parks_layer">Add a parks layer</string>
+ <string name="add_a_dynamic_geojson_source">Add a dynamic GeoJSON source</string>
+ <string name="remove_buildings_layer">Remove buildings layer</string>
+ <string name="add_a_terrain_layer">Add a terrain layer</string>
+ <string name="add_a_satellite_layer">Add a satellite layer</string>
+ <string name="change_the_water_color_on_zoom">Change the water color on zoom</string>
+ <string name="custom_tiles">Custom tiles</string>
+ <string name="apply_filtered_fill">Apply filtered fill</string>
+ <string name="apply_filtered_line">Apply filtered line</string>
+ <string name="apply_numeric_fill_filter">Apply numeric fill filter</string>
+ <string name="toggle_text_size">Toggle text size</string>
+ <string name="toggle_text_field_contents">Toggle text field contents</string>
+ <string name="toggle_text_font">Toggle text font</string>
+ <string name="zoom_in">Zoom in</string>
+ <string name="zoom_out">Zoom out</string>
+ <string name="zoom_by_2">Zoom by 2</string>
+ <string name="zoom_to_point">Zoom to point</string>
+ <string name="zoom_to_4">Zoom to 4</string>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml
new file mode 100644
index 0000000000..9ade28ae8d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="category">category</string>
+ <string name="category_basic">_Basic</string>
+ <string name="category_annotation">Annotation</string>
+ <string name="category_camera">Camera</string>
+ <string name="category_custom_layer">Custom Layer</string>
+ <string name="category_fragment">Fragment</string>
+ <string name="category_imagegenerator">Image Generator</string>
+ <string name="category_infowindow">Info Window</string>
+ <string name="category_maplayout">Map Layout</string>
+ <string name="category_offline">Offline</string>
+ <string name="category_userlocation">User Location</string>
+ <string name="category_style">Styling</string>
+ <string name="category_features">Features</string>
+ <string name="category_storage">Storage</string>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
new file mode 100644
index 0000000000..4d1f7eac38
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="description_user_location_tracking">Tracks the location of the user</string>
+ <string name="description_user_location_customization">Customize the location of the user</string>
+ <string name="description_user_location_dot_color">Customize the user location color</string>
+ <string name="description_user_location_toggle">Toggle location of the user on and off</string>
+ <string name="description_custom_location_engine">Customize location engine</string>
+ <string name="description_custom_layer">Overlay a custom native layer on the map</string>
+ <string name="description_info_window_adapter">Learn how to create a custom InfoWindow</string>
+ <string name="description_cameraposition">CameraPosition capabilities</string>
+ <string name="description_map_fragment">Showcase MapFragment</string>
+ <string name="description_map_fragment_support">Showcase SupportMapFragment</string>
+ <string name="description_multimap">Activity with multiple maps on screen</string>
+ <string name="description_press_for_marker">Add marker to map on long press</string>
+ <string name="description_camera_zoom">Different types of zoom methods</string>
+ <string name="description_minmax_zoom">Configure a max and min zoomlevel</string>
+ <string name="description_info_window">Learn how to handle the InfoWindow</string>
+ <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string>
+ <string name="description_camera_animation_types">Showcase the different animation types</string>
+ <string name="description_visible_bounds">Center the camera around a bounds</string>
+ <string name="description_dynamic_marker">Update position and icon</string>
+ <string name="description_map_padding">Map Padding example</string>
+ <string name="description_debug_mode">Debug Mode</string>
+ <string name="description_offline">Offline Map example</string>
+ <string name="description_update_metadata">Update metadata example</string>
+ <string name="description_offline_region_delete">Delete region example</string>
+ <string name="description_animated_marker">Animate the position change of a marker</string>
+ <string name="description_polyline">Add a polyline to a map</string>
+ <string name="description_polygon">Add a polygon to a map</string>
+ <string name="description_scroll_by">Scroll with pixels in x,y direction</string>
+ <string name="description_snapshot">Example to make a snapshot of the map</string>
+ <string name="description_doublemap">2 maps in a view hierarchy</string>
+ <string name="description_view_marker">Use an Android SDK View as marker</string>
+ <string name="description_dynamic_info_window_adapter">Learn how to create a dynamic custom InfoWindow</string>
+ <string name="description_viewpager">Use SupportMapFragments in a ViewPager</string>
+ <string name="description_runtime_style">Adopt the map style on the fly</string>
+ <string name="description_data_driven_style">Use functions to change the map appearance</string>
+ <string name="description_symbol_layer">Manipulate symbols at runtime</string>
+ <string name="description_custom_sprite">Use a custom sprite in a Symbol Layer</string>
+ <string name="description_geojson_clustering">Use GeoJson sources and dynamic layers to cluster information</string>
+ <string name="description_geojson_realtime">Use realtime GeoJSON data streams to move a symbol on your map</string>
+ <string name="description_print">Shows how to print a map</string>
+ <string name="description_query_rendered_feature_properties_point">Query rendered feature properties on click</string>
+ <string name="description_query_rendered_features_box_count">Count all rendered features in box</string>
+ <string name="description_query_rendered_features_box_symbol_count">Count all rendered symbols in box</string>
+ <string name="description_query_rendered_features_box_highlight">Hightligh buildings in box</string>
+ <string name="description_query_source_features">Query source for features</string>
+ <string name="description_simple_map">Shows a simple map</string>
+ <string name="description_map_change">Logs map change events to Logcat</string>
+ <string name="description_visibility_map">Changes visibility of map and view parent</string>
+ <string name="description_add_remove_markers">Change Symbol icon when zoom levels changes</string>
+ <string name="description_style_file">Use a local file as the style</string>
+ <string name="description_map_in_dialog">Display a map inside a dialog fragment</string>
+ <string name="description_marker_view_rectangle">Marker Views within a rectangle</string>
+ <string name="description_circle_layer">Show bus stops and route in Singapore</string>
+ <string name="description_url_transform">Transform urls on the fly</string>
+ <string name="description_restricted_bounds">Limit viewport to Iceland</string>
+ <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string>
+ <string name="description_building_fill_extrusion_layer">Shows how to show 3D extruded buildings</string>
+ <string name="description_animated_image_source">Shows how to animate georeferenced images</string>
+ <string name="description_bottom_sheet">Show 2 MapView on screen with a bottom sheet</string>
+ <string name="description_map_snapshotter">Show a static bitmap taken with the MapSnapshotter</string>
+ <string name="description_camera_animator">Use Android SDK Animators to animate camera position changes</string>
+ <string name="description_symbol_generator">Use Android SDK Views as symbols</string>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
index 402d42d485..0a43af09de 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <dimen name="circle_size">24dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="attr_margin">10dp</dimen>
- <dimen name="coordinatebounds_margin">32dp</dimen>
<dimen name="map_padding_left">96dp</dimen>
<dimen name="map_padding_bottom">256dp</dimen>
<dimen name="map_padding_right">32dp</dimen>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 671dccbd2b..15a916fac9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -1,189 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Mapbox Android SDK TestApp</string>
-
- <!--Activity-->
- <string name="activity_map_fragment_suport">Support Map Fragment</string>
- <string name="activity_map_fragment">Map Fragment</string>
- <string name="activity_multimap">Multiple Maps on Screen</string>
- <string name="activity_add_bulk_markers">Add Markers In Bulk</string>
- <string name="activity_animated_marker">Animated Markers</string>
- <string name="activity_dynamic_marker">Dynamic Marker</string>
- <string name="activity_polyline">Polyline</string>
- <string name="activity_polygon">Polygon</string>
- <string name="activity_press_for_marker">Press Map For Marker</string>
- <string name="activity_view_marker">View Marker API</string>
- <string name="activity_add_remove_markers">Add/Remove marker</string>
- <string name="activity_info_window">Standard InfoWindow</string>
- <string name="activity_infowindow_adapter">Custom InfoWindow</string>
- <string name="activity_dynamic_infowindow_adapter">Custom Dynamic InfoWindow</string>
- <string name="activity_camera_animation_types">Animation Types</string>
- <string name="activity_camera_zoom">Zoom Methods</string>
- <string name="activity_visible_coordinate_bounds">LatLngBounds Method</string>
- <string name="activity_camera_position">CameraPosition Method</string>
- <string name="activity_scroll_by">Scroll By Method</string>
- <string name="activity_double_map">Double Map Activity</string>
- <string name="activity_snapshot">Snapshot Activity</string>
- <string name="activity_user_tracking_mode">User tracking mode</string>
- <string name="activity_user_tracking_customization">User location drawable</string>
- <string name="activity_user_dot_color">User location tint color</string>
- <string name="activity_user_location_toggle">User location toggle</string>
- <string name="activity_custom_location_engine">Custom location engine</string>
- <string name="activity_custom_layer">Custom Layer</string>
- <string name="activity_map_padding">Map Padding</string>
- <string name="activity_debug_mode">Debug Mode</string>
- <string name="activity_offline">Offline Map</string>
- <string name="activity_update_metadata">Update metadata Map</string>
- <string name="activity_offline_region_delete">Delete region</string>
- <string name="activity_minmax_zoom">Min/Max Zoom</string>
- <string name="activity_viewpager">ViewPager</string>
- <string name="activity_runtime_style">Runtime Style</string>
- <string name="activity_data_driven_style">Data Driven Style</string>
- <string name="activity_circle_layer">Circle layer</string>
- <string name="activity_style_file">Local Style file</string>
- <string name="activity_geojson_clustering">GeoJson Clustering</string>
- <string name="activity_geojson_realtime">Add live realtime data</string>
- <string name="activity_print">Print a map</string>
- <string name="activity_query_rendered_feature_properties">Query feature properties</string>
- <string name="activity_query_rendered_features_box_count">Count features in box</string>
- <string name="activity_query_rendered_features_box_symbol_count">Count symbols in box</string>
- <string name="activity_query_rendered_features_box_highlight">Highlight features in box</string>
- <string name="activity_query_source_features">Query source features</string>
- <string name="activity_symbol_layer">Symbols</string>
- <string name="activity_add_sprite">Add Custom Sprite</string>
- <string name="activity_navigation_drawer">Android SDK View integration</string>
- <string name="activity_simple_map">Simple Map</string>
- <string name="activity_map_change">Map Change Events</string>
- <string name="activity_map_visibility">Visibility Map</string>
- <string name="activity_map_in_dialog">Dialog with map</string>
- <string name="activity_marker_view_rectangle">Marker views in rectangle</string>
- <string name="activity_url_transform">Url transform</string>
- <string name="activity_restricted_bounds">Restrict camera to a bounds</string>
- <string name="activity_fill_extrusion_layer">Fill extrusions</string>
- <string name="activity_building_fill_extrusion_layer">Building layer</string>
- <string name="activity_animated_image_source">Animated Image Source</string>
- <string name="activity_bottom_sheet">Bottom sheet</string>
-
- <!--Description-->
- <string name="description_user_location_tracking">Tracks the location of the user</string>
- <string name="description_user_location_customization">Customize the location of the user</string>
- <string name="description_user_location_dot_color">Customize the user location color</string>
- <string name="description_user_location_toggle">Toggle location of the user on and off</string>
- <string name="description_custom_location_engine">Customize location engine</string>
- <string name="description_custom_layer">Overlay a custom native layer on the map</string>
- <string name="description_info_window_adapter">Learn how to create a custom InfoWindow</string>
- <string name="description_cameraposition">CameraPosition capabilities</string>
- <string name="description_map_fragment">Showcase MapFragment</string>
- <string name="description_map_fragment_support">Showcase SupportMapFragment</string>
- <string name="description_multimap">Activity with multiple maps on screen</string>
- <string name="description_press_for_marker">Add marker to map on long press</string>
- <string name="description_camera_zoom">Different types of zoom methods</string>
- <string name="description_minmax_zoom">Configure a max and min zoomlevel</string>
- <string name="description_info_window">Learn how to handle the InfoWindow</string>
- <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string>
- <string name="description_camera_animation_types">Showcase the different animation types</string>
- <string name="description_visible_bounds">Center the camera around a bounds</string>
- <string name="description_dynamic_marker">Update position and icon</string>
- <string name="description_map_padding">Map Padding example</string>
- <string name="description_debug_mode">Debug Mode</string>
- <string name="description_offline">Offline Map example</string>
- <string name="description_update_metadata">Update metadata example</string>
- <string name="description_offline_region_delete">Delete region example</string>
- <string name="description_animated_marker">Animate the position change of a marker</string>
- <string name="description_polyline">Add a polyline to a map</string>
- <string name="description_polygon">Add a polygon to a map</string>
- <string name="description_scroll_by">Scroll with pixels in x,y direction</string>
- <string name="description_snapshot">Example to make a snapshot of the map</string>
- <string name="description_doublemap">2 maps in a view hierarchy</string>
- <string name="description_view_marker">Use an Android SDK View as marker</string>
- <string name="description_dynamic_info_window_adapter">Learn how to create a dynamic custom InfoWindow</string>
- <string name="description_viewpager">Use SupportMapFragments in a ViewPager</string>
- <string name="description_runtime_style">Adopt the map style on the fly</string>
- <string name="description_data_driven_style">Use functions to change the map appearance</string>
- <string name="description_symbol_layer">Manipulate symbols at runtime</string>
- <string name="description_custom_sprite">Use a custom sprite in a Symbol Layer</string>
- <string name="description_geojson_clustering">Use GeoJson sources and dynamic layers to cluster information</string>
- <string name="description_geojson_realtime">Use realtime GeoJSON data streams to move a symbol on your map</string>
- <string name="description_print">Shows how to print a map</string>
- <string name="description_navigation_drawer">Test animation of Android SDK View components</string>
- <string name="description_query_rendered_feature_properties_point">Query rendered feature properties on click</string>
- <string name="description_query_rendered_features_box_count">Count all rendered features in box</string>
- <string name="description_query_rendered_features_box_symbol_count">Count all rendered symbols in box</string>
- <string name="description_query_rendered_features_box_highlight">Hightligh buildings in box</string>
- <string name="description_query_source_features">Query source for features</string>
- <string name="description_simple_map">Shows a simple map</string>
- <string name="description_map_change">Logs map change events to Logcat</string>
- <string name="description_visibility_map">Changes visibility of map and view parent</string>
- <string name="description_add_remove_markers">Based on zoom level</string>
- <string name="description_style_file">Use a local file as the style</string>
- <string name="description_map_in_dialog">Display a map inside a dialog fragment</string>
- <string name="description_marker_view_rectangle">Marker Views within a rectangle</string>
- <string name="description_circle_layer">Show bus stops and route in Singapore</string>
- <string name="description_url_transform">Transform urls on the fly</string>
- <string name="description_restricted_bounds">Limit viewport to Iceland</string>
- <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string>
- <string name="description_building_fill_extrusion_layer">Shows how to show 3D extruded buildings</string>
- <string name="description_animated_image_source">Shows how to animate georeferenced images</string>
- <string name="description_bottom_sheet">Show 2 MapView on screen with a bottom sheet</string>
-
- <!--Categories-->
- <string name="category">category</string>
- <string name="category_basic">_Basic</string>
- <string name="category_annotation">Annotation</string>
- <string name="category_camera">Camera</string>
- <string name="category_custom_layer">Custom Layer</string>
- <string name="category_fragment">Fragment</string>
- <string name="category_imagegenerator">Image Generator</string>
- <string name="category_infowindow">Info Window</string>
- <string name="category_maplayout">Map Layout</string>
- <string name="category_offline">Offline</string>
- <string name="category_userlocation">User Location</string>
- <string name="category_style">Styling</string>
- <string name="category_features">Features</string>
- <string name="category_storage">Storage</string>
-
- <!--Actions-->
- <string name="action_remove_polylines">Remove polylines</string>
- <string name="action_visibility_polygon">Change visibility</string>
- <string name="action_alpha_polygon">Change alpha</string>
- <string name="action_points_polygon">Change points</string>
- <string name="action_color_polygon">Change color</string>
- <string name="action_holes_polygon">Change holes</string>
- <string name="action_width_polyline">Change width</string>
-
- <!--Menu-->
- <string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>
- <string name="menuitem_title_deselect_markers_on_tap">Deselect Markers On Tap</string>
- <string name="menuitem_title_tracking_mode_dismiss_on_gesture">Dismiss location tracking on gesture</string>
- <string name="menuitem_title_bearing_mode_dismiss_on_gesture">Dismiss bearing tracking on gesture</string>
- <string name="menuitem_title_reset">Reset</string>
- <string name="menuitem_title_rotate_gesture_enabled">Enable rotate gestures</string>
- <string name="menuitem_title_scroll_gesture_enabled">Enable scroll gestures</string>
- <string name="menuitem_title_change_location_source_lost">Change to LOST location source</string>
- <string name="menuitem_title_change_location_source_mock">Change to mock location source</string>
- <string name="menuitem_title_change_location_source_null">Reset location source to null</string>
-
- <!--Button-->
- <string name="button_camera_move">Move</string>
- <string name="button_camera_ease">Ease</string>
- <string name="button_camera_animate">Animate</string>
- <string name="button_user_dot_default">Default</string>
- <string name="button_user_dot_tint">Tint dot</string>
- <string name="button_user_accuracy_ring_tint">Tint ring</string>
- <string name="button_user_transparent_tint">tran</string>
- <string name="button_open_dialog">Open dialog</string>
- <string name="button_download_region">Download region</string>
- <string name="button_list_regions">List regions</string>
-
- <!--Other-->
- <string name="navigation_drawer_open">Open navigation drawer</string>
- <string name="navigation_drawer_close">Close navigation drawer</string>
- <string name="scrollby_x_value">X: %1$d</string>
- <string name="scrollby_y_value">Y: %1$d</string>
- <string name="dialog_camera_position">Animate to new position</string>
- <string name="dynamic_marker_chelsea_title">Chelsea</string>
- <string name="dynamic_marker_chelsea_snippet">Stamford Bridge</string>
- <string name="dynamic_marker_arsenal_title">Arsenal</string>
- <string name="dynamic_marker_arsenal_snippet">Emirates Stadium</string>
- <string name="debug_zoom">Zoom: %s</string>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
new file mode 100644
index 0000000000..8f394d0eb4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="activity_map_fragment_suport">Support Map Fragment</string>
+ <string name="activity_map_fragment">Map Fragment</string>
+ <string name="activity_multimap">Multiple Maps on Screen</string>
+ <string name="activity_add_bulk_markers">Add Markers In Bulk</string>
+ <string name="activity_animated_marker">Animated Markers</string>
+ <string name="activity_dynamic_marker">Dynamic Marker</string>
+ <string name="activity_polyline">Polyline</string>
+ <string name="activity_polygon">Polygon</string>
+ <string name="activity_press_for_marker">Press Map For Marker</string>
+ <string name="activity_view_marker">View Marker API</string>
+ <string name="activity_add_remove_markers">Zoom function with SymbolLayer</string>
+ <string name="activity_info_window">Standard InfoWindow</string>
+ <string name="activity_infowindow_adapter">Custom InfoWindow</string>
+ <string name="activity_dynamic_infowindow_adapter">Custom Dynamic InfoWindow</string>
+ <string name="activity_camera_animation_types">Animation Types</string>
+ <string name="activity_camera_zoom">Zoom Methods</string>
+ <string name="activity_visible_coordinate_bounds">LatLngBounds Method</string>
+ <string name="activity_camera_position">CameraPosition Method</string>
+ <string name="activity_scroll_by">Scroll By Method</string>
+ <string name="activity_double_map">Double Map Activity</string>
+ <string name="activity_snapshot">Snapshot Activity</string>
+ <string name="activity_user_tracking_mode">User tracking mode</string>
+ <string name="activity_user_tracking_customization">User location drawable</string>
+ <string name="activity_user_dot_color">User location tint color</string>
+ <string name="activity_user_location_toggle">User location toggle</string>
+ <string name="activity_custom_location_engine">Custom location engine</string>
+ <string name="activity_custom_layer">Custom Layer</string>
+ <string name="activity_map_padding">Map Padding</string>
+ <string name="activity_debug_mode">Debug Mode</string>
+ <string name="activity_offline">Offline Map</string>
+ <string name="activity_update_metadata">Update metadata Map</string>
+ <string name="activity_offline_region_delete">Delete region</string>
+ <string name="activity_minmax_zoom">Min/Max Zoom</string>
+ <string name="activity_viewpager">ViewPager</string>
+ <string name="activity_runtime_style">Runtime Style</string>
+ <string name="activity_data_driven_style">Data Driven Style</string>
+ <string name="activity_circle_layer">Circle layer</string>
+ <string name="activity_style_file">Local Style file</string>
+ <string name="activity_geojson_clustering">GeoJson Clustering</string>
+ <string name="activity_geojson_realtime">Add live realtime data</string>
+ <string name="activity_print">Print a map</string>
+ <string name="activity_query_rendered_feature_properties">Query feature properties</string>
+ <string name="activity_query_rendered_features_box_count">Count features in box</string>
+ <string name="activity_query_rendered_features_box_symbol_count">Count symbols in box</string>
+ <string name="activity_query_rendered_features_box_highlight">Highlight features in box</string>
+ <string name="activity_query_source_features">Query source features</string>
+ <string name="activity_symbol_layer">Symbols</string>
+ <string name="activity_add_sprite">Add Custom Sprite</string>
+ <string name="activity_simple_map">Simple Map</string>
+ <string name="activity_map_change">Map Change Events</string>
+ <string name="activity_map_visibility">Visibility Map</string>
+ <string name="activity_map_in_dialog">Dialog with map</string>
+ <string name="activity_marker_view_rectangle">Marker views in rectangle</string>
+ <string name="activity_url_transform">Url transform</string>
+ <string name="activity_restricted_bounds">Restrict camera to a bounds</string>
+ <string name="activity_fill_extrusion_layer">Fill extrusions</string>
+ <string name="activity_building_fill_extrusion_layer">Building layer</string>
+ <string name="activity_animated_image_source">Animated Image Source</string>
+ <string name="activity_bottom_sheet">Bottom sheet</string>
+ <string name="activity_map_snapshotter">Map Snapshotter</string>
+ <string name="activity_camera_animator">Animator animation</string>
+ <string name="activity_symbol_generator">SymbolGenerator</string>
+</resources> \ No newline at end of file
diff --git a/platform/android/README.md b/platform/android/README.md
index 8ff259142d..2b7ad0bcc9 100644
--- a/platform/android/README.md
+++ b/platform/android/README.md
@@ -1,6 +1,6 @@
# [Mapbox Android SDK](https://www.mapbox.com/android-sdk/)
-[![Bitrise](https://www.bitrise.io/app/79cdcbdc42de4303.svg?token=_InPF8bII6W7J6kFr-L8QQ&branch=master)](https://www.bitrise.io/app/79cdcbdc42de4303)
+[![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master)
A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Java applications on Android devices.
@@ -109,6 +109,10 @@ make aproj
Open Android Studio project in `/platform/android`, run `make android-configuration` in the root folder of the project.
+##### Setup Checkstyle
+
+Mapbox uses specific IDE settings related to code and check style.
+See [checkstyle guide](https://github.com/mapbox/mapbox-gl-native/wiki/Setting-up-Mapbox-checkstyle) for configuration details.
##### Setting Mapbox Access Token
@@ -121,3 +125,8 @@ With the first gradle invocation, gradle will take the value of the `MAPBOX_ACCE
Run the configuration for the `MapboxGLAndroidSDKTestApp` module and select a device or emulator to deploy on. Based on the selected device, the c++ code will be compiled for the related processor architecture. You can see the project compiling in the `View > Tool Windows > Gradle Console`.
More information about building and distributing this project in [DISTRIBUTE.md][https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/DISTRIBUTE.md].
+
+#### Symbolicating native crashes
+
+When hitting native crashes you can use ndk-stack to symbolicate crashes.
+More information in [this](https://github.com/mapbox/mapbox-gl-native/wiki/Getting-line-numbers-from-an-Android-crash-with-ndk-stack) guide. \ No newline at end of file
diff --git a/platform/android/build.gradle b/platform/android/build.gradle
index bc90896812..e298b84da8 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -12,6 +12,7 @@ buildscript {
allprojects {
repositories {
jcenter()
+ maven { url 'https://maven.google.com' }
maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
}
}
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index 390e4842f4..8dd537d36e 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -31,25 +31,6 @@ macro(mbgl_platform_core)
PRIVATE platform/android/src/run_loop_impl.hpp
PRIVATE platform/android/src/timer.cpp
- # File source
- PRIVATE platform/android/src/http_file_source.cpp
- PRIVATE platform/android/src/asset_manager.hpp
- PRIVATE platform/android/src/asset_manager_file_source.cpp
- PRIVATE platform/android/src/asset_manager_file_source.hpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
-
# Misc
PRIVATE platform/android/src/logging_android.cpp
PRIVATE platform/android/src/thread.cpp
@@ -73,15 +54,28 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/util/default_thread_pool.hpp
# Rendering
+ PRIVATE platform/android/src/android_renderer_backend.cpp
+ PRIVATE platform/android/src/android_renderer_backend.hpp
PRIVATE platform/android/src/android_renderer_frontend.cpp
PRIVATE platform/android/src/android_renderer_frontend.hpp
+
+ # Snapshots
+ PRIVATE platform/default/mbgl/gl/headless_backend.cpp
+ PRIVATE platform/default/mbgl/gl/headless_backend.hpp
+ PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
+ PRIVATE platform/default/mbgl/gl/headless_frontend.hpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.hpp
+ PRIVATE platform/linux/src/headless_backend_egl.cpp
+ PRIVATE platform/linux/src/headless_display_egl.cpp
+ PRIVATE platform/android/src/snapshotter/map_snapshotter.cpp
+ PRIVATE platform/android/src/snapshotter/map_snapshotter.hpp
)
target_include_directories(mbgl-core
PUBLIC platform/default
)
- target_add_mason_package(mbgl-core PUBLIC sqlite)
target_add_mason_package(mbgl-core PUBLIC nunicode)
target_add_mason_package(mbgl-core PUBLIC geojson)
target_add_mason_package(mbgl-core PUBLIC jni.hpp)
@@ -106,6 +100,37 @@ macro(mbgl_platform_core)
)
endmacro()
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/android/src/http_file_source.cpp
+ PRIVATE platform/android/src/asset_manager.hpp
+ PRIVATE platform/android/src/asset_manager_file_source.cpp
+ PRIVATE platform/android/src/asset_manager_file_source.hpp
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_add_mason_package(mbgl-filesource PUBLIC sqlite)
+ target_add_mason_package(mbgl-filesource PUBLIC jni.hpp)
+
+ target_compile_options(mbgl-filesource
+ PRIVATE -fvisibility=hidden
+ PRIVATE -ffunction-sections
+ PRIVATE -fdata-sections
+ )
+
+ target_link_libraries(mbgl-filesource
+ PUBLIC -llog
+ PUBLIC -landroid
+ PUBLIC -lstdc++
+ PUBLIC -latomic
+ )
+endmacro()
+
+
## Main library ##
add_library(mbgl-android STATIC
@@ -191,6 +216,10 @@ add_library(mbgl-android STATIC
# Native map
platform/android/src/native_map_view.cpp
platform/android/src/native_map_view.hpp
+ platform/android/src/map_renderer.cpp
+ platform/android/src/map_renderer.hpp
+ platform/android/src/map_renderer_runnable.cpp
+ platform/android/src/map_renderer_runnable.hpp
# Java core classes
platform/android/src/java/util.cpp
@@ -282,6 +311,7 @@ target_compile_options(mbgl-android
)
target_link_libraries(mbgl-android
+ PUBLIC mbgl-filesource
PUBLIC mbgl-core
)
diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle
index 509b2a41c0..521084559f 100644
--- a/platform/android/dependencies.gradle
+++ b/platform/android/dependencies.gradle
@@ -7,11 +7,11 @@ ext {
versionCode = 11
versionName = "5.0.0"
- mapboxServicesVersion = "2.1.3"
- supportLibVersion = "25.3.1"
- espressoVersion = '2.2.2'
- testRunnerVersion = '0.5'
- leakCanaryVersion = '1.5'
+ mapboxServicesVersion = "2.2.3"
+ supportLibVersion = "25.4.0"
+ espressoVersion = '3.0.1'
+ testRunnerVersion = '1.0.1'
+ leakCanaryVersion = '1.5.1'
dep = [
// mapbox
@@ -24,10 +24,10 @@ ext {
// unit test
junit : 'junit:junit:4.12',
- mockito : 'org.mockito:mockito-core:2.2.27',
+ mockito : 'org.mockito:mockito-core:2.10.0',
// instrumentation test
- testSpoonRunner : 'com.squareup.spoon:spoon-client:1.6.2',
+ testSpoonRunner : 'com.squareup.spoon:spoon-client:1.7.1',
testRunner : "com.android.support.test:runner:${testRunnerVersion}",
testRules : "com.android.support.test:rules:${testRunnerVersion}",
testEspressoCore : "com.android.support.test.espresso:espresso-core:${espressoVersion}",
@@ -42,9 +42,8 @@ ext {
// square crew
timber : 'com.jakewharton.timber:timber:4.5.1',
- okhttp3 : 'com.squareup.okhttp3:okhttp:3.8.0',
+ okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.0',
leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",
- leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}",
- leakCanaryTest : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}"
+ leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}"
]
} \ No newline at end of file
diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js
index a8b2c98c86..abc0796bc1 100644
--- a/platform/android/scripts/generate-style-code.js
+++ b/platform/android/scripts/generate-style-code.js
@@ -111,6 +111,9 @@ global.propertyNativeType = function (property) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
+ if (/^(text|icon)-anchor$/.test(property.name)) {
+ return 'SymbolAnchorType';
+ }
switch (property.type) {
case 'boolean':
return 'bool';
@@ -267,6 +270,9 @@ global.evaluatedType = function (property) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
+ if (/^(text|icon)-anchor$/.test(property.name)) {
+ return 'SymbolAnchorType';
+ }
if (/position/.test(property.name)) {
return 'Position';
}
diff --git a/platform/android/src/android_renderer_backend.cpp b/platform/android/src/android_renderer_backend.cpp
new file mode 100755
index 0000000000..9e49915650
--- /dev/null
+++ b/platform/android/src/android_renderer_backend.cpp
@@ -0,0 +1,63 @@
+#include "android_renderer_backend.hpp"
+
+#include <mbgl/gl/context.hpp>
+
+#include <EGL/egl.h>
+
+#include <cassert>
+
+namespace mbgl {
+namespace android {
+
+/**
+ * From mbgl::View
+ */
+void AndroidRendererBackend::bind() {
+ assert(BackendScope::exists());
+ setFramebufferBinding(0);
+ setViewport(0, 0, getFramebufferSize());
+}
+
+/**
+ * From mbgl::RendererBackend.
+ */
+gl::ProcAddress AndroidRendererBackend::initializeExtension(const char* name) {
+ assert(BackendScope::exists());
+ return eglGetProcAddress(name);
+}
+
+void AndroidRendererBackend::updateViewPort() {
+ assert(BackendScope::exists());
+ setViewport(0, 0, getFramebufferSize());
+}
+
+void AndroidRendererBackend::resizeFramebuffer(int width, int height) {
+ fbWidth = width;
+ fbHeight = height;
+}
+
+PremultipliedImage AndroidRendererBackend::readFramebuffer() const {
+ assert(BackendScope::exists());
+ return RendererBackend::readFramebuffer(getFramebufferSize());
+}
+
+mbgl::Size AndroidRendererBackend::getFramebufferSize() const {
+ return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) };
+}
+
+/**
+ * From mbgl::RendererBackend.
+ */
+void AndroidRendererBackend::updateAssumedState() {
+ assumeFramebufferBinding(0);
+ assumeViewport(0, 0, getFramebufferSize());
+}
+
+void AndroidRendererBackend::markContextLost() {
+ if (context) {
+ context->setCleanupOnDestruction(false);
+ }
+}
+
+} // namespace android
+} // namspace mbgl
diff --git a/platform/android/src/android_renderer_backend.hpp b/platform/android/src/android_renderer_backend.hpp
new file mode 100755
index 0000000000..c5c552459f
--- /dev/null
+++ b/platform/android/src/android_renderer_backend.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <mbgl/renderer/renderer_backend.hpp>
+
+namespace mbgl {
+namespace android {
+
+class AndroidRendererBackend : public RendererBackend {
+public:
+
+ // mbgl::RendererBackend //
+ void bind() override;
+ void updateAssumedState() override;
+ mbgl::Size getFramebufferSize() const override;
+
+ // Ensures the current context is not
+ // cleaned up when destroyed
+ void markContextLost();
+
+ void updateViewPort();
+
+ void resizeFramebuffer(int width, int height);
+ PremultipliedImage readFramebuffer() const;
+
+protected:
+ // mbgl::RendererBackend //
+ gl::ProcAddress initializeExtension(const char*) override;
+ void activate() override {};
+ void deactivate() override {};
+
+
+private:
+
+ // Minimum texture size according to OpenGL ES 2.0 specification.
+ int fbWidth = 64;
+ int fbHeight = 64;
+
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp
index 597bebe40d..b80e23e21f 100644
--- a/platform/android/src/android_renderer_frontend.cpp
+++ b/platform/android/src/android_renderer_frontend.cpp
@@ -1,72 +1,115 @@
#include "android_renderer_frontend.hpp"
-#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include "android_renderer_backend.hpp"
namespace mbgl {
namespace android {
-AndroidRendererFrontend::AndroidRendererFrontend(
- std::unique_ptr<Renderer> renderer_,
- RendererBackend& backend_,
- InvalidateCallback invalidate)
- : renderer(std::move(renderer_))
- , backend(backend_)
- , asyncInvalidate([=, invalidate=std::move(invalidate)]() {
- invalidate();
- }) {
+// Forwards RendererObserver signals to the given
+// Delegate RendererObserver on the given RunLoop
+class ForwardingRendererObserver : public RendererObserver {
+public:
+ ForwardingRendererObserver(util::RunLoop& mapRunLoop, RendererObserver& delegate_)
+ : mailbox(std::make_shared<Mailbox>(mapRunLoop))
+ , delegate(delegate_, mailbox) {
+ }
+
+ ~ForwardingRendererObserver() {
+ mailbox->close();
+ }
+
+ void onInvalidate() override {
+ delegate.invoke(&RendererObserver::onInvalidate);
+ }
+
+ void onResourceError(std::exception_ptr err) override {
+ delegate.invoke(&RendererObserver::onResourceError, err);
+ }
+
+ void onWillStartRenderingMap() override {
+ delegate.invoke(&RendererObserver::onWillStartRenderingMap);
+ }
+
+ void onWillStartRenderingFrame() override {
+ delegate.invoke(&RendererObserver::onWillStartRenderingFrame);
+ }
+
+ void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) override {
+ delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded);
+ }
+
+ void onDidFinishRenderingMap() override {
+ delegate.invoke(&RendererObserver::onDidFinishRenderingMap);
+ }
+
+private:
+ std::shared_ptr<Mailbox> mailbox;
+ ActorRef<RendererObserver> delegate;
+};
+
+AndroidRendererFrontend::AndroidRendererFrontend(MapRenderer& mapRenderer_)
+ : mapRenderer(mapRenderer_)
+ , mapRunLoop(util::RunLoop::Get()) {
}
AndroidRendererFrontend::~AndroidRendererFrontend() = default;
void AndroidRendererFrontend::reset() {
- assert (renderer);
- if (renderer) {
- renderer.reset();
- }
+ mapRenderer.reset();
}
void AndroidRendererFrontend::setObserver(RendererObserver& observer) {
- assert (renderer);
- renderer->setObserver(&observer);
+ assert (util::RunLoop::Get());
+ // Don't call the Renderer directly, but use MapRenderer#setObserver to make sure
+ // the Renderer may be re-initialised without losing the RendererObserver reference.
+ mapRenderer.setObserver(std::make_unique<ForwardingRendererObserver>(*mapRunLoop, observer));
}
void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) {
- updateParameters = std::move(params);
- asyncInvalidate.send();
-}
-
-void AndroidRendererFrontend::render() {
- assert (renderer);
- if (!updateParameters) return;
-
- BackendScope guard { backend };
-
- renderer->render(*updateParameters);
+ mapRenderer.update(std::move(params));
+ mapRenderer.requestRender();
}
void AndroidRendererFrontend::onLowMemory() {
- assert (renderer);
- renderer->onLowMemory();
+ mapRenderer.actor().invoke(&Renderer::onLowMemory);
}
std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID,
const SourceQueryOptions& options) const {
- return renderer->querySourceFeatures(sourceID, options);
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(&Renderer::querySourceFeatures, sourceID, options).get();
}
std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box,
const RenderedQueryOptions& options) const {
- return renderer->queryRenderedFeatures(box, options);
+
+ // Select the right overloaded method
+ std::vector<Feature> (Renderer::*fn)(const ScreenBox&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures;
+
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(fn, box, options).get();
}
std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point,
const RenderedQueryOptions& options) const {
- return renderer->queryRenderedFeatures(point, options);
+
+ // Select the right overloaded method
+ std::vector<Feature> (Renderer::*fn)(const ScreenCoordinate&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures;
+
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(fn, point, options).get();
}
AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& box) const {
- return renderer->queryPointAnnotations(box);
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(&Renderer::queryPointAnnotations, box).get();
}
} // namespace android
diff --git a/platform/android/src/android_renderer_frontend.hpp b/platform/android/src/android_renderer_frontend.hpp
index 107bbbe0b6..94508fd816 100644
--- a/platform/android/src/android_renderer_frontend.hpp
+++ b/platform/android/src/android_renderer_frontend.hpp
@@ -1,19 +1,20 @@
-
#pragma once
+#include <mbgl/actor/actor.hpp>
#include <mbgl/annotation/annotation.hpp>
-#include <mbgl/renderer/renderer_backend.hpp>
#include <mbgl/renderer/renderer_frontend.hpp>
-#include <mbgl/util/async_task.hpp>
#include <mbgl/util/geo.hpp>
+#include <mbgl/util/run_loop.hpp>
#include <functional>
#include <memory>
#include <vector>
+#include <string>
+
+#include "map_renderer.hpp"
namespace mbgl {
-class Renderer;
class RenderedQueryOptions;
class SourceQueryOptions;
@@ -21,15 +22,14 @@ namespace android {
class AndroidRendererFrontend : public RendererFrontend {
public:
- using InvalidateCallback = std::function<void ()>;
- AndroidRendererFrontend(std::unique_ptr<Renderer>, RendererBackend&, InvalidateCallback);
+
+ AndroidRendererFrontend(MapRenderer&);
~AndroidRendererFrontend() override;
void reset() override;
void setObserver(RendererObserver&) override;
void update(std::shared_ptr<UpdateParameters>) override;
- void render();
// Feature querying
std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions&) const;
@@ -41,10 +41,8 @@ public:
void onLowMemory();
private:
- std::unique_ptr<Renderer> renderer;
- RendererBackend& backend;
- std::shared_ptr<UpdateParameters> updateParameters;
- util::AsyncTask asyncInvalidate;
+ MapRenderer& mapRenderer;
+ util::RunLoop* mapRunLoop;
};
} // namespace android
diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp
index c55c9c3527..1ed68d0835 100644
--- a/platform/android/src/example_custom_layer.cpp
+++ b/platform/android/src/example_custom_layer.cpp
@@ -92,6 +92,10 @@ void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters&
reinterpret_cast<ExampleCustomLayer*>(context)->render();
}
+void nativeContextLost(void */*context*/) {
+ mbgl::Log::Info(mbgl::Event::General, "nativeContextLost");
+}
+
void nativeDenitialize(void *context) {
mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize");
delete reinterpret_cast<ExampleCustomLayer*>(context);
@@ -123,6 +127,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
reinterpret_cast<jlong>(nativeRender));
env->SetStaticLongField(customLayerClass,
+ env->GetStaticFieldID(customLayerClass, "ContextLostFunction", "J"),
+ reinterpret_cast<jlong>(nativeContextLost));
+
+ env->SetStaticLongField(customLayerClass,
env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"),
reinterpret_cast<jlong>(nativeDenitialize));
diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp
index 5d19c506bc..262e3d3c6a 100644
--- a/platform/android/src/file_source.cpp
+++ b/platform/android/src/file_source.cpp
@@ -1,9 +1,9 @@
#include "file_source.hpp"
#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/util/logging.hpp>
-#include <mbgl/util/run_loop.hpp>
#include "asset_manager_file_source.hpp"
#include "jni/generic_global_ref_deleter.hpp"
@@ -45,7 +45,7 @@ void FileSource::setAPIBaseUrl(jni::JNIEnv& env, jni::String url) {
void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> transformCallback) {
if (transformCallback) {
- resourceTransform = std::make_unique<Actor<ResourceTransform>>(*util::RunLoop::Get(),
+ resourceTransform = std::make_unique<Actor<ResourceTransform>>(*Scheduler::GetCurrent(),
// Capture the ResourceTransformCallback object as a managed global into
// the lambda. It is released automatically when we're setting a new ResourceTransform in
// a subsequent call.
diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp
index ee1429bd74..8eb9416e9d 100644
--- a/platform/android/src/http_file_source.cpp
+++ b/platform/android/src/http_file_source.cpp
@@ -117,7 +117,9 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code,
}
if (cacheControl) {
- response.expires = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()).toTimePoint();
+ const auto cc = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str());
+ response.expires = cc.toTimePoint();
+ response.mustRevalidate = cc.mustRevalidate;
}
if (expires) {
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index db8dd1dbdf..6acb6a3664 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -32,6 +32,8 @@
#include "gson/json_object.hpp"
#include "gson/json_primitive.hpp"
#include "java_types.hpp"
+#include "map_renderer.hpp"
+#include "map_renderer_runnable.hpp"
#include "native_map_view.hpp"
#include "offline/offline_manager.hpp"
#include "offline/offline_region.hpp"
@@ -47,6 +49,7 @@
#include "style/layers/layers.hpp"
#include "style/sources/sources.hpp"
#include "style/light.hpp"
+#include "snapshotter/map_snapshotter.hpp"
namespace mbgl {
namespace android {
@@ -143,6 +146,8 @@ void registerNatives(JavaVM *vm) {
Polyline::registerNative(env);
// Map
+ MapRenderer::registerNative(env);
+ MapRendererRunnable::registerNative(env);
NativeMapView::registerNative(env);
// Http
@@ -177,6 +182,9 @@ void registerNatives(JavaVM *vm) {
OfflineTilePyramidRegionDefinition::registerNative(env);
OfflineRegionError::registerNative(env);
OfflineRegionStatus::registerNative(env);
+
+ // Snapshotter
+ MapSnapshotter::registerNative(env);
}
} // namespace android
diff --git a/platform/android/src/jni/generic_global_ref_deleter.hpp b/platform/android/src/jni/generic_global_ref_deleter.hpp
index 4e53e0a0ce..7239e361a7 100644
--- a/platform/android/src/jni/generic_global_ref_deleter.hpp
+++ b/platform/android/src/jni/generic_global_ref_deleter.hpp
@@ -18,5 +18,34 @@ struct GenericGlobalRefDeleter {
}
};
+
+template < class TagType >
+class GenericWeakObjectRefDeleter;
+
+template < class TagType = jni::ObjectTag >
+using GenericUniqueWeakObject = std::unique_ptr< const jni::Object<TagType>, GenericWeakObjectRefDeleter<TagType> >;
+
+template < class TagType >
+class GenericWeakObjectRefDeleter
+{
+public:
+ using pointer = jni::PointerToValue< jni::Object<TagType> >;
+
+ void operator()(pointer p) const
+ {
+ if (p)
+ {
+ auto env = AttachEnv();
+ env->DeleteWeakGlobalRef(jni::Unwrap(p->Get()));
+ }
+ }
+};
+
+template < class TagType >
+GenericUniqueWeakObject<TagType> SeizeGenericWeakRef(JNIEnv&, jni::Object<TagType>&& object)
+{
+ return GenericUniqueWeakObject<TagType>(jni::PointerToValue<jni::Object<TagType>>(std::move(object)), GenericWeakObjectRefDeleter<TagType>());
+};
+
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp
index d6f2cb83e8..1fc5f9789f 100644
--- a/platform/android/src/map/camera_position.cpp
+++ b/platform/android/src/map/camera_position.cpp
@@ -27,6 +27,24 @@ jni::Object<CameraPosition> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp
return CameraPosition::javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees);
}
+mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, jni::Object<CameraPosition> position) {
+ static auto bearing = CameraPosition::javaClass.GetField<jni::jdouble>(env, "bearing");
+ static auto target = CameraPosition::javaClass.GetField<jni::Object<LatLng>>(env, "target");
+ static auto tilt = CameraPosition::javaClass.GetField<jni::jdouble>(env, "tilt");
+ static auto zoom = CameraPosition::javaClass.GetField<jni::jdouble>(env, "zoom");
+
+ auto center = LatLng::getLatLng(env, position.Get(env, target));
+
+ return mbgl::CameraOptions {
+ center,
+ {},
+ {},
+ position.Get(env, zoom),
+ position.Get(env, bearing) * util::DEG2RAD,
+ position.Get(env, tilt)
+ };
+}
+
void CameraPosition::registerNative(jni::JNIEnv &env) {
// Lookup the class
CameraPosition::javaClass = *jni::Class<CameraPosition>::Find(env).NewGlobalRef(env).release();
diff --git a/platform/android/src/map/camera_position.hpp b/platform/android/src/map/camera_position.hpp
index b9f1646cc9..4eee8be758 100644
--- a/platform/android/src/map/camera_position.hpp
+++ b/platform/android/src/map/camera_position.hpp
@@ -15,6 +15,8 @@ public:
static jni::Object<CameraPosition> New(jni::JNIEnv&, mbgl::CameraOptions);
+ static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, jni::Object<CameraPosition>);
+
static jni::Class<CameraPosition> javaClass;
static void registerNative(jni::JNIEnv&);
diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp
new file mode 100644
index 0000000000..7655455210
--- /dev/null
+++ b/platform/android/src/map_renderer.cpp
@@ -0,0 +1,200 @@
+#include "map_renderer.hpp"
+
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <string>
+
+#include "attach_env.hpp"
+#include "android_renderer_backend.hpp"
+#include "map_renderer_runnable.hpp"
+#include "file_source.hpp"
+
+namespace mbgl {
+namespace android {
+
+MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object<MapRenderer> obj,
+ jni::Object<FileSource> _fileSource, jni::jfloat pixelRatio_,
+ jni::String programCacheDir_)
+ : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapRenderer>(jni::NewWeakGlobalRef(_env, obj.Get()).release()))), pixelRatio(pixelRatio_)
+ , fileSource(FileSource::getDefaultFileSource(_env, _fileSource))
+ , programCacheDir(jni::Make<std::string>(_env, programCacheDir_))
+ , threadPool(sharedThreadPool())
+ , mailbox(std::make_shared<Mailbox>(*this)) {
+}
+
+MapRenderer::~MapRenderer() = default;
+
+void MapRenderer::reset() {
+ // Make sure to destroy the renderer on the GL Thread
+ auto self = ActorRef<MapRenderer>(*this, mailbox);
+ self.ask(&MapRenderer::resetRenderer).wait();
+
+ // Lock to make sure there is no concurrent initialisation on the gl thread
+ std::lock_guard<std::mutex> lock(initialisationMutex);
+ rendererObserver.reset();
+}
+
+ActorRef<Renderer> MapRenderer::actor() const {
+ return *rendererRef;
+}
+
+void MapRenderer::schedule(std::weak_ptr<Mailbox> scheduled) {
+ // Create a runnable and schedule it on the gl thread
+ android::UniqueEnv _env = android::AttachEnv();
+ auto runnable = std::make_unique<MapRendererRunnable>(*_env, std::move(scheduled));
+
+ static auto queueEvent = javaClass.GetMethod<void(
+ jni::Object<MapRendererRunnable>)>(*_env, "queueEvent");
+ javaPeer->Call(*_env, queueEvent, runnable->getPeer());
+
+ // Release the object as it will be destroyed on GC of the Java Peer
+ runnable.release();
+}
+
+void MapRenderer::requestRender() {
+ android::UniqueEnv _env = android::AttachEnv();
+ static auto onInvalidate = javaClass.GetMethod<void()>(*_env, "requestRender");
+ javaPeer->Call(*_env, onInvalidate);
+}
+
+void MapRenderer::update(std::shared_ptr<UpdateParameters> params) {
+ // Lock on the parameters
+ std::lock_guard<std::mutex> lock(updateMutex);
+ updateParameters = std::move(params);
+}
+
+void MapRenderer::setObserver(std::shared_ptr<RendererObserver> _rendererObserver) {
+ // Lock as the initialization can come from the main thread or the GL thread first
+ std::lock_guard<std::mutex> lock(initialisationMutex);
+
+ rendererObserver = std::move(_rendererObserver);
+
+ // Set the new observer on the Renderer implementation
+ if (renderer) {
+ renderer->setObserver(rendererObserver.get());
+ }
+}
+
+void MapRenderer::requestSnapshot(SnapshotCallback callback) {
+ auto self = ActorRef<MapRenderer>(*this, mailbox);
+ self.invoke(
+ &MapRenderer::scheduleSnapshot,
+ std::make_unique<SnapshotCallback>([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) {
+ runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable {
+ callback(std::move(image));
+ });
+ snapshotCallback.reset();
+ })
+ );
+}
+
+// Called on OpenGL thread //
+
+void MapRenderer::resetRenderer() {
+ assert (renderer);
+ renderer.reset();
+}
+
+void MapRenderer::scheduleSnapshot(std::unique_ptr<SnapshotCallback> callback) {
+ snapshotCallback = std::move(callback);
+ requestRender();
+}
+
+void MapRenderer::render(JNIEnv&) {
+ assert (renderer);
+
+ std::shared_ptr<UpdateParameters> params;
+ {
+ // Lock on the parameters
+ std::unique_lock<std::mutex> lock(updateMutex);
+ if (!updateParameters) return;
+
+ // Hold on to the update parameters during render
+ params = updateParameters;
+ }
+
+ // Activate the backend
+ BackendScope backendGuard { *backend };
+
+ // Ensure that the "current" scheduler on the render thread is
+ // this scheduler.
+ Scheduler::SetCurrent(this);
+
+ if (framebufferSizeChanged) {
+ backend->updateViewPort();
+ framebufferSizeChanged = false;
+ }
+
+ renderer->render(*params);
+
+ // Deliver the snapshot if requested
+ if (snapshotCallback) {
+ snapshotCallback->operator()(backend->readFramebuffer());
+ snapshotCallback.reset();
+ }
+}
+
+void MapRenderer::onSurfaceCreated(JNIEnv&) {
+ // Lock as the initialization can come from the main thread or the GL thread first
+ std::lock_guard<std::mutex> lock(initialisationMutex);
+
+ // The android system will have already destroyed the underlying
+ // GL resources if this is not the first intialization and an
+ // attempt to clean them up will fail
+ if (backend) backend->markContextLost();
+ if (renderer) renderer->markContextLost();
+
+ // Reset in opposite order
+ renderer.reset();
+ backend.reset();
+
+ // Create the new backend and renderer
+ backend = std::make_unique<AndroidRendererBackend>();
+ renderer = std::make_unique<Renderer>(*backend, pixelRatio, fileSource, *threadPool,
+ GLContextMode::Unique, programCacheDir);
+ rendererRef = std::make_unique<ActorRef<Renderer>>(*renderer, mailbox);
+
+ // Set the observer on the new Renderer implementation
+ if (rendererObserver) {
+ renderer->setObserver(rendererObserver.get());
+ }
+}
+
+void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) {
+ backend->resizeFramebuffer(width, height);
+ framebufferSizeChanged = true;
+ requestRender();
+}
+
+// Static methods //
+
+jni::Class<MapRenderer> MapRenderer::javaClass;
+
+void MapRenderer::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ MapRenderer::javaClass = *jni::Class<MapRenderer>::Find(env).NewGlobalRef(env).release();
+
+#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<MapRenderer>(env, MapRenderer::javaClass, "nativePtr",
+ std::make_unique<MapRenderer, JNIEnv&, jni::Object<MapRenderer>, jni::Object<FileSource>, jni::jfloat, jni::String>,
+ "nativeInitialize", "finalize",
+ METHOD(&MapRenderer::render, "nativeRender"),
+ METHOD(&MapRenderer::onSurfaceCreated,
+ "nativeOnSurfaceCreated"),
+ METHOD(&MapRenderer::onSurfaceChanged,
+ "nativeOnSurfaceChanged"));
+}
+
+MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object<MapRenderer> jObject) {
+ static auto field = MapRenderer::javaClass.GetField<jlong>(env, "nativePtr");
+ MapRenderer* mapRenderer = reinterpret_cast<MapRenderer*>(jObject.Get(env, field));
+ assert(mapRenderer != nullptr);
+ return *mapRenderer;
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp
new file mode 100644
index 0000000000..0d614912a9
--- /dev/null
+++ b/platform/android/src/map_renderer.hpp
@@ -0,0 +1,126 @@
+#pragma once
+
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/util/image.hpp>
+
+#include <memory>
+#include <utility>
+
+#include <jni/jni.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+
+#include "jni/generic_global_ref_deleter.hpp"
+
+namespace mbgl {
+
+template <class>
+class ActorRef;
+class Mailbox;
+class Renderer;
+class RendererBackend;
+class RendererObserver;
+class ThreadPool;
+class UpdateParameters;
+
+namespace android {
+
+class AndroidRendererBackend;
+class FileSource;
+
+/**
+ * The MapRenderer is a peer class that encapsulates the actions
+ * performed on the GL Thread.
+ *
+ * The public methods are safe to call from the main thread, others are not.
+ */
+class MapRenderer : public Scheduler {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRenderer"; };
+
+ static jni::Class<MapRenderer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ static MapRenderer& getNativePeer(JNIEnv&, jni::Object<MapRenderer>);
+
+ MapRenderer(jni::JNIEnv& _env,
+ jni::Object<MapRenderer>,
+ jni::Object<FileSource>,
+ jni::jfloat pixelRatio,
+ jni::String programCacheDir);
+
+ ~MapRenderer() override;
+
+ // Resets the renderer to clean up on the calling thread
+ void reset();
+
+ // Takes the RendererObserver by shared_ptr so we
+ // don't have to make the header public. Use
+ // this instead of Renderer#setObserver directly
+ void setObserver(std::shared_ptr<RendererObserver>);
+
+ // Sets the new update parameters to use on subsequent
+ // renders. Be sure to trigger a render with
+ // requestRender().
+ void update(std::shared_ptr<UpdateParameters>);
+
+ // Gives a handle to the Renderer to enable actions on
+ // any thread.
+ ActorRef<Renderer> actor() const;
+
+ // From Scheduler. Schedules by using callbacks to the
+ // JVM to process the mailbox on the right thread.
+ void schedule(std::weak_ptr<Mailbox> scheduled) override;
+
+ void requestRender();
+
+ // Snapshot - requires a RunLoop on the calling thread
+ using SnapshotCallback = std::function<void (PremultipliedImage)>;
+ void requestSnapshot(SnapshotCallback);
+
+protected:
+ // Called from the GL Thread //
+
+ void scheduleSnapshot(std::unique_ptr<SnapshotCallback>);
+
+private:
+ // Called from the GL Thread //
+
+ // Resets the renderer
+ void resetRenderer();
+
+ // Renders a frame.
+ void render(JNIEnv&);
+
+ void onSurfaceCreated(JNIEnv&);
+
+ void onSurfaceChanged(JNIEnv&, jint width, jint height);
+
+private:
+ GenericUniqueWeakObject<MapRenderer> javaPeer;
+
+ float pixelRatio;
+ DefaultFileSource& fileSource;
+ std::string programCacheDir;
+
+ std::shared_ptr<ThreadPool> threadPool;
+ std::shared_ptr<Mailbox> mailbox;
+
+ std::mutex initialisationMutex;
+ std::shared_ptr<RendererObserver> rendererObserver;
+
+ std::unique_ptr<AndroidRendererBackend> backend;
+ std::unique_ptr<Renderer> renderer;
+ std::unique_ptr<ActorRef<Renderer>> rendererRef;
+
+ std::shared_ptr<UpdateParameters> updateParameters;
+ std::mutex updateMutex;
+
+ bool framebufferSizeChanged = false;
+
+ std::unique_ptr<SnapshotCallback> snapshotCallback;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/map_renderer_runnable.cpp b/platform/android/src/map_renderer_runnable.cpp
new file mode 100644
index 0000000000..df8cba5e55
--- /dev/null
+++ b/platform/android/src/map_renderer_runnable.cpp
@@ -0,0 +1,49 @@
+#include "map_renderer_runnable.hpp"
+
+#include <mbgl/util/logging.hpp>
+
+namespace mbgl {
+namespace android {
+
+MapRendererRunnable::MapRendererRunnable(jni::JNIEnv& env, std::weak_ptr<Mailbox> mailbox_)
+ : mailbox(std::move(mailbox_)) {
+
+ // Create the Java peer
+ jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 5);
+ static auto constructor = javaClass.GetConstructor<jlong>(env);
+ auto instance = javaClass.New(env, constructor, reinterpret_cast<jlong>(this));
+ javaPeer = SeizeGenericWeakRef(env, jni::Object<MapRendererRunnable>(jni::NewWeakGlobalRef(env, instance.Get()).release()));
+}
+
+MapRendererRunnable::~MapRendererRunnable() = default;
+
+void MapRendererRunnable::run(jni::JNIEnv&) {
+ Mailbox::maybeReceive(mailbox);
+}
+
+jni::Object<MapRendererRunnable> MapRendererRunnable::getPeer() {
+ return *javaPeer;
+}
+
+// Static methods //
+
+jni::Class<MapRendererRunnable> MapRendererRunnable::javaClass;
+
+void MapRendererRunnable::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ MapRendererRunnable::javaClass = *jni::Class<MapRendererRunnable>::Find(env).NewGlobalRef(env).release();
+
+#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ jni::RegisterNativePeer<MapRendererRunnable>(
+ env,
+ MapRendererRunnable::javaClass,
+ "nativePtr",
+ std::make_unique<MapRendererRunnable, JNIEnv&>,
+ "nativeInitialize",
+ "finalize",
+ METHOD(&MapRendererRunnable::run, "run"));
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/map_renderer_runnable.hpp b/platform/android/src/map_renderer_runnable.hpp
new file mode 100644
index 0000000000..75646a442d
--- /dev/null
+++ b/platform/android/src/map_renderer_runnable.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include <memory>
+#include <utility>
+
+#include <jni/jni.hpp>
+
+#include "jni/generic_global_ref_deleter.hpp"
+
+namespace mbgl {
+namespace android {
+
+/**
+ * The MapRendererRunnable is a peer class that encapsulates
+ * a scheduled mailbox in a Java Runnable so it can be
+ * scheduled on the map renderer thread.
+ *
+ */
+class MapRendererRunnable {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable"; };
+
+ static jni::Class<MapRendererRunnable> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ MapRendererRunnable(jni::JNIEnv&, std::weak_ptr<Mailbox>);
+
+ // Only for jni registration, unused
+ MapRendererRunnable(jni::JNIEnv&) {
+ assert(false);
+ }
+
+ ~MapRendererRunnable();
+
+ void run(jni::JNIEnv&);
+
+ jni::Object<MapRendererRunnable> getPeer();
+
+private:
+ GenericUniqueWeakObject<MapRendererRunnable> javaPeer;
+ std::weak_ptr<Mailbox> mailbox;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index fa9e90ddc6..24a35a7068 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -9,12 +9,10 @@
#include <sys/system_properties.h>
-#include <EGL/egl.h>
#include <android/native_window_jni.h>
#include <jni/jni.hpp>
-#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/event.hpp>
@@ -28,7 +26,7 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/filter.hpp>
-#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/query.hpp>
// Java -> C++ conversion
#include "style/android_conversion.hpp"
@@ -43,13 +41,16 @@
#include "jni.hpp"
#include "attach_env.hpp"
+#include "map_renderer.hpp"
#include "android_renderer_frontend.hpp"
+#include "file_source.hpp"
#include "bitmap.hpp"
#include "run_loop_impl.hpp"
#include "java/util.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
#include "style/light.hpp"
+#include "bitmap_factory.hpp"
namespace mbgl {
namespace android {
@@ -57,11 +58,12 @@ namespace android {
NativeMapView::NativeMapView(jni::JNIEnv& _env,
jni::Object<NativeMapView> _obj,
jni::Object<FileSource> jFileSource,
- jni::jfloat _pixelRatio,
- jni::String _programCacheDir)
- : javaPeer(_obj.NewWeakGlobalRef(_env)),
- pixelRatio(_pixelRatio),
- threadPool(sharedThreadPool()) {
+ jni::Object<MapRenderer> jMapRenderer,
+ jni::jfloat _pixelRatio)
+ : javaPeer(_obj.NewWeakGlobalRef(_env))
+ , mapRenderer(MapRenderer::getNativePeer(_env, jMapRenderer))
+ , pixelRatio(_pixelRatio)
+ , threadPool(sharedThreadPool()) {
// Get a reference to the JavaVM for callbacks
if (_env.GetJavaVM(&vm) < 0) {
@@ -69,17 +71,11 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
return;
}
- auto& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource);
-
- // Create a renderer
- auto renderer = std::make_unique<Renderer>(*this, pixelRatio, fileSource, *threadPool,
- GLContextMode::Unique,
- jni::Make<std::string>(_env, _programCacheDir));
+ // Get native peer for file source
+ mbgl::FileSource& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource);
// Create a renderer frontend
- rendererFrontend = std::make_unique<AndroidRendererFrontend>(std::move(renderer),
- *this,
- [this] { this->invalidate(); });
+ rendererFrontend = std::make_unique<AndroidRendererFrontend>(mapRenderer);
// Create the core map
map = std::make_unique<mbgl::Map>(*rendererFrontend, *this,
@@ -87,100 +83,17 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
static_cast<uint32_t>(height) }, pixelRatio,
fileSource, *threadPool, MapMode::Continuous,
ConstrainMode::HeightOnly, ViewportMode::Default);
-
- // initialize egl components
- _initializeDisplay();
- _initializeContext();
}
/**
* Called through NativeMapView#destroy()
*/
NativeMapView::~NativeMapView() {
- _terminateContext();
- _destroySurface();
- _terminateDisplay();
-
map.reset();
-
vm = nullptr;
}
/**
- * From mbgl::RendererBackend
- */
-void NativeMapView::bind() {
- setFramebufferBinding(0);
- setViewport(0, 0, getFramebufferSize());
-}
-
-/**
- * From mbgl::RendererBackend.
- */
-gl::ProcAddress NativeMapView::initializeExtension(const char* name) {
- return eglGetProcAddress(name);
-}
-
-void NativeMapView::activate() {
-
- oldDisplay = eglGetCurrentDisplay();
- oldReadSurface = eglGetCurrentSurface(EGL_READ);
- oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
- oldContext = eglGetCurrentContext();
-
- assert(vm != nullptr);
-
- if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) {
- if (!eglMakeCurrent(display, surface, surface, context)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
-
- if (!eglSwapInterval(display, 0)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError());
- throw std::runtime_error("eglSwapInterval() failed");
- }
- } else {
- mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready");
- }
-}
-
-/**
- * From mbgl::RendererBackend.
- */
-void NativeMapView::deactivate() {
- assert(vm != nullptr);
-
- if (oldContext != context && oldContext != EGL_NO_CONTEXT) {
- if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
- } else if (display != EGL_NO_DISPLAY) {
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
- } else {
- mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready");
- }
-}
-
-/**
- * From mbgl::RendererBackend. Callback to java NativeMapView#onInvalidate().
- *
- * May be called from any thread
- */
-void NativeMapView::invalidate() {
- android::UniqueEnv _env = android::AttachEnv();
- static auto onInvalidate = javaClass.GetMethod<void ()>(*_env, "onInvalidate");
- javaPeer->Call(*_env, onInvalidate);
-}
-
-/**
* From mbgl::RendererBackend. Callback to java NativeMapView#onMapChanged(int).
*
* May be called from any thread
@@ -259,67 +172,12 @@ void NativeMapView::onSourceChanged(mbgl::style::Source&) {
// JNI Methods //
-void NativeMapView::createSurface(jni::JNIEnv& env, jni::Object<> _surface) {
- _createSurface(ANativeWindow_fromSurface(&env, jni::Unwrap(*_surface)));
-}
-
-void NativeMapView::destroySurface(jni::JNIEnv&) {
- _destroySurface();
-}
-
-void NativeMapView::render(jni::JNIEnv& env) {
- BackendScope guard { *this };
-
- if (framebufferSizeChanged) {
- setViewport(0, 0, getFramebufferSize());
- framebufferSizeChanged = false;
- }
-
- rendererFrontend->render();
-
- if(snapshot){
- snapshot = false;
-
- // take snapshot
- auto image = readFramebuffer(getFramebufferSize());
- auto bitmap = Bitmap::CreateBitmap(env, std::move(image));
-
- // invoke Mapview#OnSnapshotReady
- android::UniqueEnv _env = android::AttachEnv();
- static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
- javaPeer->Call(*_env, onSnapshotReady, bitmap);
- }
-
- if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
- if (!eglSwapBuffers(display, surface)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
- eglGetError());
- throw std::runtime_error("eglSwapBuffers() failed");
- }
-
- updateFps();
- } else {
- mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
- }
-}
-
-void NativeMapView::update(jni::JNIEnv&) {
- invalidate();
-}
-
void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) {
width = util::max(64, w);
height = util::max(64, h);
map->setSize({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
}
-void NativeMapView::resizeFramebuffer(jni::JNIEnv&, int w, int h) {
- fbWidth = w;
- fbHeight = h;
- framebufferSizeChanged = true;
- invalidate();
-}
-
jni::String NativeMapView::getStyleUrl(jni::JNIEnv& env) {
return jni::Make<jni::String>(env, map->getStyle().getURL());
}
@@ -433,6 +291,11 @@ jni::Object<CameraPosition> NativeMapView::getCameraForLatLngBounds(jni::JNIEnv&
return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), insets));
}
+jni::Object<CameraPosition> NativeMapView::getCameraForGeometry(jni::JNIEnv& env, jni::Object<geojson::Geometry> jGeometry, double bearing) {
+ auto geometry = geojson::Geometry::convert(env, jGeometry);
+ return CameraPosition::New(env, map->cameraForGeometry(geometry, insets, bearing));
+}
+
void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) {
if (reachable) {
mbgl::NetworkStatus::Reachable();
@@ -537,11 +400,15 @@ void NativeMapView::setContentPadding(JNIEnv&, double top, double left, double b
}
void NativeMapView::scheduleSnapshot(jni::JNIEnv&) {
- snapshot = true;
-}
+ mapRenderer.requestSnapshot([&](PremultipliedImage image) {
+ auto _env = android::AttachEnv();
+ // Convert image to bitmap
+ auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image));
-void NativeMapView::enableFps(jni::JNIEnv&, jni::jboolean enable) {
- fpsEnabled = enable;
+ // invoke Mapview#OnSnapshotReady
+ static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
+ javaPeer->Call(*_env, onSnapshotReady, bitmap);
+ });
}
jni::Object<CameraPosition> NativeMapView::getCameraPosition(jni::JNIEnv& env) {
@@ -591,12 +458,10 @@ void NativeMapView::setDebug(JNIEnv&, jni::jboolean debug) {
DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision
: DebugOptions::NoDebug;
map->setDebug(debugOptions);
- fpsEnabled = debug;
}
void NativeMapView::cycleDebugOptions(JNIEnv&) {
map->cycleDebugOptions();
- fpsEnabled = map->getDebug() != DebugOptions::NoDebug;
}
jni::jboolean NativeMapView::getDebug(JNIEnv&) {
@@ -716,6 +581,11 @@ void NativeMapView::addAnnotationIcon(JNIEnv& env, jni::String symbol, jint w, j
symbolName, std::move(premultipliedImage), float(scale)));
}
+void NativeMapView::removeAnnotationIcon(JNIEnv& env, jni::String symbol) {
+ const std::string symbolName = jni::Make<std::string>(env, symbol);
+ map->removeAnnotationImage(symbolName);
+}
+
jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::String symbolName) {
return map->getTopOffsetPixelsForAnnotationImage(jni::Make<std::string>(env, symbolName));
}
@@ -1038,6 +908,14 @@ void NativeMapView::removeImage(JNIEnv& env, jni::String name) {
map->getStyle().removeImage(jni::Make<std::string>(env, name));
}
+jni::Object<Bitmap> NativeMapView::getImage(JNIEnv& env, jni::String name) {
+ const mbgl::style::Image *image = map->getStyle().getImage(jni::Make<std::string>(env, name));
+ if (image) {
+ return Bitmap::CreateBitmap(env, image->getImage());
+ } else {
+ return jni::Object<Bitmap>();
+ }
+}
void NativeMapView::setPrefetchesTiles(JNIEnv&, jni::jboolean enable) {
map->setPrefetchZoomDelta(enable ? util::DEFAULT_PREFETCH_ZOOM_DELTA : uint8_t(0));
@@ -1047,409 +925,6 @@ jni::jboolean NativeMapView::getPrefetchesTiles(JNIEnv&) {
return jni::jboolean(map->getPrefetchZoomDelta() > 0);
}
-// Private methods //
-
-void NativeMapView::_initializeDisplay() {
- assert(display == EGL_NO_DISPLAY);
- assert(config == nullptr);
- assert(format < 0);
-
- display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (display == EGL_NO_DISPLAY) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError());
- throw std::runtime_error("eglGetDisplay() failed");
- }
-
- EGLint major, minor;
- if (!eglInitialize(display, &major, &minor)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError());
- throw std::runtime_error("eglInitialize() failed");
- }
- if ((major <= 1) && (minor < 3)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major,
- minor);
- throw std::runtime_error("EGL version is too low");
- }
-
- // Detect if we are in emulator.
- const bool inEmulator = []() {
- char prop[PROP_VALUE_MAX];
- __system_property_get("ro.kernel.qemu", prop);
- return strtol(prop, nullptr, 0) == 1;
- }();
-
- if (inEmulator) {
- // XXX https://code.google.com/p/android/issues/detail?id=78977
- mbgl::Log::Warning(mbgl::Event::Android, "Running SDK in emulator!");
- }
-
- if (!eglBindAPI(EGL_OPENGL_ES_API)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglBindAPI(EGL_OPENGL_ES_API) returned error %d", eglGetError());
- throw std::runtime_error("eglBindAPI() failed");
- }
-
- // Get all configs at least RGB 565 with 16 depth and 8 stencil
- EGLint configAttribs[] = {
- EGL_CONFIG_CAVEAT, EGL_NONE,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_BUFFER_SIZE, 16,
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5,
- EGL_DEPTH_SIZE, 16,
- EGL_STENCIL_SIZE, 8,
- (inEmulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT,
- (inEmulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER,
- EGL_NONE
- };
-
- EGLint numConfigs;
- if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d",
- eglGetError());
- throw std::runtime_error("eglChooseConfig() failed");
- }
- if (numConfigs < 1) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs.");
- throw std::runtime_error("eglChooseConfig() failed");
- }
-
- const auto configs = std::make_unique<EGLConfig[]>(numConfigs);
- if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
- throw std::runtime_error("eglChooseConfig() failed");
- }
-
- config = chooseConfig(configs.get(), numConfigs);
- if (config == nullptr) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen");
- throw std::runtime_error("No config chosen");
- }
-
- if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-}
-
-// Quality
-typedef enum {
- Format16Bit = 3,
- Format32BitNoAlpha = 1,
- Format32BitAlpha = 2,
- Format24Bit = 0,
- Unknown = 4
-} BufferFormat;
-
-typedef enum {
- Format16Depth8Stencil = 1,
- Format24Depth8Stencil = 0,
-} DepthStencilFormat;
-
-// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num,
-// config_id>
-typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties;
-
-EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) {
- // Create a list of configs that pass our filters
- std::list<ConfigProperties> configList;
- for (int i = 0; i < numConfigs; i++) {
- EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil,
- sampleBuffers, samples;
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- bool configOk = true;
- configOk &= (depth == 24) || (depth == 16);
- configOk &= stencil == 8;
- configOk &= sampleBuffers == 0;
- configOk &= samples == 0;
-
- // Filter our configs first for depth, stencil and anti-aliasing
- if (configOk) {
- // Work out the config's buffer format
- BufferFormat bufferFormat;
- if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) {
- bufferFormat = Format16Bit;
- } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
- bufferFormat = Format32BitNoAlpha;
- } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
- bufferFormat = Format32BitAlpha;
- } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
- bufferFormat = Format24Bit;
- } else {
- bufferFormat = Unknown;
- }
-
- // Work out the config's depth stencil format
- DepthStencilFormat depthStencilFormat;
- if ((depth == 16) && (stencil == 8)) {
- depthStencilFormat = Format16Depth8Stencil;
- } else {
- depthStencilFormat = Format24Depth8Stencil;
- }
-
- bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
- bool isCaveat = caveat != EGL_NONE;
- EGLConfig configId = configs[i];
-
- // Ignore formats we don't recognise
- if (bufferFormat != Unknown) {
- configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat,
- isNotConformant, isCaveat, i, configId));
- }
- }
- }
-
- if (configList.empty()) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty.");
- }
-
- // Sort the configs to find the best one
- configList.sort();
- bool isConformant = !std::get<2>(configList.front());
- bool isCaveat = std::get<3>(configList.front());
- EGLConfig configId = std::get<5>(configList.front());
-
- if (isCaveat) {
- mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat.");
- }
- if (!isConformant) {
- mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant.");
- }
-
- return configId;
-}
-
-void NativeMapView::_terminateDisplay() {
- if (display != EGL_NO_DISPLAY) {
- // Destroy the surface first, if it still exists. This call needs a valid surface.
- if (surface != EGL_NO_SURFACE) {
- if (!eglDestroySurface(display, surface)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
- eglGetError());
- throw std::runtime_error("eglDestroySurface() failed");
- }
- surface = EGL_NO_SURFACE;
- }
-
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
-
- if (!eglTerminate(display)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d",
- eglGetError());
- throw std::runtime_error("eglTerminate() failed");
- }
- }
-
- display = EGL_NO_DISPLAY;
- config = nullptr;
- format = -1;
-}
-
-void NativeMapView::_initializeContext() {
- assert(display != EGL_NO_DISPLAY);
- assert(context == EGL_NO_CONTEXT);
- assert(config != nullptr);
-
- const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
- context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
- if (context == EGL_NO_CONTEXT) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d",
- eglGetError());
- throw std::runtime_error("eglCreateContext() failed");
- }
-}
-
-void NativeMapView::_terminateContext() {
- if (display != EGL_NO_DISPLAY) {
-
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
-
- if (context != EGL_NO_CONTEXT) {
- if (!eglDestroyContext(display, context)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d",
- eglGetError());
- throw std::runtime_error("eglDestroyContext() failed");
- }
- }
- }
-
- context = EGL_NO_CONTEXT;
-}
-
-void NativeMapView::_createSurface(ANativeWindow *window_) {
- assert(window == nullptr);
- assert(window_ != nullptr);
- window = window_;
-
- assert(display != EGL_NO_DISPLAY);
- assert(surface == EGL_NO_SURFACE);
- assert(config != nullptr);
- assert(format >= 0);
-
- ANativeWindow_setBuffersGeometry(window, 0, 0, format);
-
- const EGLint surfaceAttribs[] = {EGL_NONE};
- surface = eglCreateWindowSurface(display, config, window, surfaceAttribs);
- if (surface == EGL_NO_SURFACE) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d",
- eglGetError());
- throw std::runtime_error("eglCreateWindowSurface() failed");
- }
-
- if (firstRender) {
- firstRender = false;
-
- BackendScope guard(*this);
-
- if (!eglMakeCurrent(display, surface, surface, context)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
- }
-}
-
-void NativeMapView::_destroySurface() {
- if (surface != EGL_NO_SURFACE) {
- if (!eglDestroySurface(display, surface)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
- eglGetError());
- throw std::runtime_error("eglDestroySurface() failed");
- }
- }
-
- surface = EGL_NO_SURFACE;
- firstRender = true;
-
- if (window != nullptr) {
- ANativeWindow_release(window);
- window = nullptr;
- }
-}
-
-mbgl::Size NativeMapView::getFramebufferSize() const {
- return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) };
-}
-
-void NativeMapView::updateAssumedState() {
- assumeFramebufferBinding(0);
- assumeViewport(0, 0, getFramebufferSize());
-}
-
-void NativeMapView::updateFps() {
- if (!fpsEnabled) {
- return;
- }
-
- static int frames = 0;
- static int64_t timeElapsed = 0LL;
-
- frames++;
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- int64_t currentTime = now.tv_sec * 1000000000LL + now.tv_nsec;
-
- if (currentTime - timeElapsed >= 1) {
- fps = frames / ((currentTime - timeElapsed) / 1E9);
- mbgl::Log::Info(mbgl::Event::Render, "FPS: %4.2f", fps);
- timeElapsed = currentTime;
- frames = 0;
- }
-
- assert(vm != nullptr);
-
- android::UniqueEnv _env = android::AttachEnv();
- static auto onFpsChanged = javaClass.GetMethod<void (double)>(*_env, "onFpsChanged");
- javaPeer->Call(*_env, onFpsChanged, fps);
-}
-
// Static methods //
jni::Class<NativeMapView> NativeMapView::javaClass;
@@ -1462,15 +937,10 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
// Register the peer
jni::RegisterNativePeer<NativeMapView>(env, NativeMapView::javaClass, "nativePtr",
- std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::String>,
+ std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::Object<MapRenderer>, jni::jfloat>,
"nativeInitialize",
"nativeDestroy",
- METHOD(&NativeMapView::render, "nativeRender"),
- METHOD(&NativeMapView::update, "nativeUpdate"),
METHOD(&NativeMapView::resizeView, "nativeResizeView"),
- METHOD(&NativeMapView::resizeFramebuffer, "nativeResizeFramebuffer"),
- METHOD(&NativeMapView::createSurface, "nativeCreateSurface"),
- METHOD(&NativeMapView::destroySurface, "nativeDestroySurface"),
METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"),
METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"),
METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"),
@@ -1484,6 +954,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::getLatLng, "nativeGetLatLng"),
METHOD(&NativeMapView::setLatLng, "nativeSetLatLng"),
METHOD(&NativeMapView::getCameraForLatLngBounds, "nativeGetCameraForLatLngBounds"),
+ METHOD(&NativeMapView::getCameraForGeometry, "nativeGetCameraForGeometry"),
METHOD(&NativeMapView::setReachability, "nativeSetReachability"),
METHOD(&NativeMapView::resetPosition, "nativeResetPosition"),
METHOD(&NativeMapView::getPitch, "nativeGetPitch"),
@@ -1503,7 +974,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"),
METHOD(&NativeMapView::setContentPadding, "nativeSetContentPadding"),
METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"),
- METHOD(&NativeMapView::enableFps, "nativeSetEnableFps"),
METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"),
METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"),
METHOD(&NativeMapView::addMarkers, "nativeAddMarkers"),
@@ -1523,6 +993,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::updatePolygon, "nativeUpdatePolygon"),
METHOD(&NativeMapView::removeAnnotations, "nativeRemoveAnnotations"),
METHOD(&NativeMapView::addAnnotationIcon, "nativeAddAnnotationIcon"),
+ METHOD(&NativeMapView::removeAnnotationIcon, "nativeRemoveAnnotationIcon"),
METHOD(&NativeMapView::getTopOffsetPixelsForAnnotationSymbol, "nativeGetTopOffsetPixelsForAnnotationSymbol"),
METHOD(&NativeMapView::getTransitionDuration, "nativeGetTransitionDuration"),
METHOD(&NativeMapView::setTransitionDuration, "nativeSetTransitionDuration"),
@@ -1547,11 +1018,12 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::removeSource, "nativeRemoveSource"),
METHOD(&NativeMapView::addImage, "nativeAddImage"),
METHOD(&NativeMapView::removeImage, "nativeRemoveImage"),
+ METHOD(&NativeMapView::getImage, "nativeGetImage"),
METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds"),
METHOD(&NativeMapView::setPrefetchesTiles, "nativeSetPrefetchesTiles"),
METHOD(&NativeMapView::getPrefetchesTiles, "nativeGetPrefetchesTiles")
);
}
-}
-}
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index ba1545728a..4d226d0fa9 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <mbgl/renderer/renderer_backend.hpp>
#include <mbgl/map/change.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
@@ -10,13 +9,13 @@
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
-#include "file_source.hpp"
#include "annotation/marker.hpp"
#include "annotation/polygon.hpp"
#include "annotation/polyline.hpp"
#include "graphics/pointf.hpp"
#include "graphics/rectf.hpp"
#include "geojson/feature.hpp"
+#include "geojson/geometry.hpp"
#include "geometry/lat_lng.hpp"
#include "geometry/projected_meters.hpp"
#include "style/layers/layers.hpp"
@@ -24,6 +23,7 @@
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
#include "style/light.hpp"
+#include "bitmap.hpp"
#include <exception>
#include <string>
@@ -36,8 +36,10 @@ namespace mbgl {
namespace android {
class AndroidRendererFrontend;
+class FileSource;
+class MapRenderer;
-class NativeMapView : public RendererBackend, public MapObserver {
+class NativeMapView : public MapObserver {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; };
@@ -49,16 +51,11 @@ public:
NativeMapView(jni::JNIEnv&,
jni::Object<NativeMapView>,
jni::Object<FileSource>,
- jni::jfloat pixelRatio,
- jni::String programCacheDir);
+ jni::Object<MapRenderer>,
+ jni::jfloat pixelRatio);
virtual ~NativeMapView();
- // mbgl::RendererBackend //
-
- void bind() override;
- void updateAssumedState() override;
-
// Deprecated //
void notifyMapChange(mbgl::MapChange);
@@ -76,23 +73,10 @@ public:
void onDidFinishLoadingStyle() override;
void onSourceChanged(mbgl::style::Source&) override;
- // Signal the view system, we want to redraw
- void invalidate();
-
// JNI //
- void render(jni::JNIEnv&);
-
- void update(jni::JNIEnv&);
-
void resizeView(jni::JNIEnv&, int, int);
- void resizeFramebuffer(jni::JNIEnv&, int, int);
-
- void createSurface(jni::JNIEnv&, jni::Object<>);
-
- void destroySurface(jni::JNIEnv&);
-
jni::String getStyleUrl(jni::JNIEnv&);
void setStyleUrl(jni::JNIEnv&, jni::String);
@@ -121,6 +105,8 @@ public:
jni::Object<CameraPosition> getCameraForLatLngBounds(jni::JNIEnv&, jni::Object<mbgl::android::LatLngBounds>);
+ jni::Object<CameraPosition> getCameraForGeometry(jni::JNIEnv&, jni::Object<geojson::Geometry>, double bearing);
+
void setReachability(jni::JNIEnv&, jni::jboolean);
void resetPosition(jni::JNIEnv&);
@@ -159,8 +145,6 @@ public:
void scheduleSnapshot(jni::JNIEnv&);
- void enableFps(jni::JNIEnv&, jni::jboolean enable);
-
jni::Object<CameraPosition> getCameraPosition(jni::JNIEnv&);
void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, jni::String);
@@ -199,6 +183,8 @@ public:
void addAnnotationIcon(JNIEnv&, jni::String, jint, jint, jfloat, jni::Array<jbyte>);
+ void removeAnnotationIcon(JNIEnv&, jni::String);
+
jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, jni::String);
jni::jlong getTransitionDuration(JNIEnv&);
@@ -251,73 +237,28 @@ public:
void removeImage(JNIEnv&, jni::String);
+ jni::Object<Bitmap> getImage(JNIEnv&, jni::String);
+
void setPrefetchesTiles(JNIEnv&, jni::jboolean);
jni::jboolean getPrefetchesTiles(JNIEnv&);
-protected:
- // mbgl::RendererBackend //
-
- gl::ProcAddress initializeExtension(const char*) override;
- void activate() override;
- void deactivate() override;
-
-private:
- void _initializeDisplay();
-
- void _terminateDisplay();
-
- void _initializeContext();
-
- void _terminateContext();
-
- void _createSurface(ANativeWindow*);
-
- void _destroySurface();
-
- EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
-
- mbgl::Size getFramebufferSize() const;
-
- void updateFps();
-
private:
std::unique_ptr<AndroidRendererFrontend> rendererFrontend;
JavaVM *vm = nullptr;
jni::UniqueWeakObject<NativeMapView> javaPeer;
+ MapRenderer& mapRenderer;
+
std::string styleUrl;
std::string apiKey;
- ANativeWindow *window = nullptr;
-
- EGLConfig config = nullptr;
- EGLint format = -1;
-
- EGLDisplay oldDisplay = EGL_NO_DISPLAY;
- EGLSurface oldReadSurface = EGL_NO_SURFACE;
- EGLSurface oldDrawSurface = EGL_NO_SURFACE;
- EGLContext oldContext = EGL_NO_CONTEXT;
-
- EGLDisplay display = EGL_NO_DISPLAY;
- EGLSurface surface = EGL_NO_SURFACE;
- EGLContext context = EGL_NO_CONTEXT;
-
-
float pixelRatio;
- bool fpsEnabled = false;
- bool snapshot = false;
- bool firstRender = true;
- double fps = 0.0;
// Minimum texture size according to OpenGL ES 2.0 specification.
int width = 64;
int height = 64;
- int fbWidth = 64;
- int fbHeight = 64;
-
- bool framebufferSizeChanged = true;
// Ensure these are initialised last
std::shared_ptr<mbgl::ThreadPool> threadPool;
diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp
index 3c605b70e8..dff7d1d984 100644
--- a/platform/android/src/run_loop.cpp
+++ b/platform/android/src/run_loop.cpp
@@ -4,6 +4,7 @@
#include <mbgl/util/thread_local.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/timer.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <android/looper.h>
@@ -23,7 +24,6 @@
namespace {
using namespace mbgl::util;
-static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
int looperCallbackNew(int fd, int, void* data) {
int buffer[1];
@@ -200,19 +200,20 @@ Milliseconds RunLoop::Impl::processRunnables() {
}
RunLoop* RunLoop::Get() {
- return current.get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>(this, type)) {
- current.set(this);
+ Scheduler::SetCurrent(this);
}
RunLoop::~RunLoop() {
- current.set(nullptr);
+ Scheduler::SetCurrent(nullptr);
}
LOOP_HANDLE RunLoop::getLoopHandle() {
- return current.get()->impl.get();
+ return Get()->impl.get();
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp
new file mode 100644
index 0000000000..d64218d11a
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshotter.cpp
@@ -0,0 +1,110 @@
+#include "map_snapshotter.hpp"
+
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include "../attach_env.hpp"
+#include "../bitmap.hpp"
+
+namespace mbgl {
+namespace android {
+
+MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env,
+ jni::Object<MapSnapshotter> _obj,
+ jni::Object<FileSource> jFileSource,
+ jni::jfloat _pixelRatio,
+ jni::jint width,
+ jni::jint height,
+ jni::String styleURL,
+ jni::Object<LatLngBounds> region,
+ jni::Object<CameraPosition> position,
+ jni::String _programCacheDir)
+ : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapSnapshotter>(jni::NewWeakGlobalRef(_env, _obj.Get()).release())))
+ , pixelRatio(_pixelRatio)
+ , threadPool(sharedThreadPool()) {
+
+ // Get a reference to the JavaVM for callbacks
+ if (_env.GetJavaVM(&vm) < 0) {
+ _env.ExceptionDescribe();
+ return;
+ }
+
+ auto& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource);
+ auto size = mbgl::Size { static_cast<uint32_t>(width), static_cast<uint32_t>(height) };
+ auto cameraOptions = position ? CameraPosition::getCameraOptions(_env, position) : CameraOptions();
+ optional<mbgl::LatLngBounds> bounds;
+ if (region) {
+ bounds = LatLngBounds::getLatLngBounds(_env, region);
+ }
+
+ // Create the core snapshotter
+ snapshotter = std::make_unique<mbgl::MapSnapshotter>(fileSource,
+ *threadPool,
+ jni::Make<std::string>(_env, styleURL),
+ size,
+ pixelRatio,
+ cameraOptions,
+ bounds,
+ jni::Make<std::string>(_env, _programCacheDir));
+
+}
+
+MapSnapshotter::~MapSnapshotter() = default;
+
+void MapSnapshotter::start(JNIEnv&) {
+ MBGL_VERIFY_THREAD(tid);
+
+ snapshotCallback = std::make_unique<Actor<mbgl::MapSnapshotter::Callback>>(*Scheduler::GetCurrent(), [this](std::exception_ptr err, PremultipliedImage image) {
+ MBGL_VERIFY_THREAD(tid);
+ android::UniqueEnv _env = android::AttachEnv();
+
+ if (err) {
+ // error handler callback
+ static auto onSnapshotFailed = javaClass.GetMethod<void (jni::String)>(*_env, "onSnapshotFailed");
+ javaPeer->Call(*_env, onSnapshotFailed, jni::Make<jni::String>(*_env, util::toString(err)));
+ } else {
+ // Create the bitmap
+ auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image));
+
+ // invoke callback
+ static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
+ javaPeer->Call(*_env, onSnapshotReady, bitmap);
+ }
+ });
+
+ snapshotter->snapshot(snapshotCallback->self());
+}
+
+void MapSnapshotter::cancel(JNIEnv&) {
+ MBGL_VERIFY_THREAD(tid);
+
+ snapshotCallback.reset();
+ snapshotter.reset();
+}
+
+// Static methods //
+
+jni::Class<MapSnapshotter> MapSnapshotter::javaClass;
+
+void MapSnapshotter::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ MapSnapshotter::javaClass = *jni::Class<MapSnapshotter>::Find(env).NewGlobalRef(env).release();
+
+#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<MapSnapshotter>(env, MapSnapshotter::javaClass, "nativePtr",
+ std::make_unique<MapSnapshotter, JNIEnv&, jni::Object<MapSnapshotter>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jint, jni::String, jni::Object<LatLngBounds>, jni::Object<CameraPosition>, jni::String>,
+ "nativeInitialize",
+ "finalize",
+ METHOD(&MapSnapshotter::start, "nativeStart"),
+ METHOD(&MapSnapshotter::cancel, "nativeCancel")
+ );
+}
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/snapshotter/map_snapshotter.hpp b/platform/android/src/snapshotter/map_snapshotter.hpp
new file mode 100644
index 0000000000..093f589c05
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshotter.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <mbgl/map/map_snapshotter.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/util.hpp>
+
+#include "../file_source.hpp"
+#include "../geometry/lat_lng_bounds.hpp"
+#include "../map/camera_position.hpp"
+
+#include <jni/jni.hpp>
+#include "../jni/generic_global_ref_deleter.hpp"
+
+#include <memory>
+
+namespace mbgl {
+namespace android {
+
+class SnapshotterRendererFrontend;
+
+class MapSnapshotter {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/snapshotter/MapSnapshotter"; };
+
+ static jni::Class<MapSnapshotter> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ MapSnapshotter(jni::JNIEnv&,
+ jni::Object<MapSnapshotter>,
+ jni::Object<FileSource>,
+ jni::jfloat pixelRatio,
+ jni::jint width,
+ jni::jint height,
+ jni::String styleURL,
+ jni::Object<LatLngBounds> region,
+ jni::Object<CameraPosition> position,
+ jni::String programCacheDir);
+
+ ~MapSnapshotter();
+
+ void start(JNIEnv&);
+
+ void cancel(JNIEnv&);
+
+private:
+ MBGL_STORE_THREAD(tid);
+
+ JavaVM *vm = nullptr;
+ GenericUniqueWeakObject<MapSnapshotter> javaPeer;
+
+ float pixelRatio;
+
+ std::shared_ptr<mbgl::ThreadPool> threadPool;
+ std::unique_ptr<Actor<mbgl::MapSnapshotter::Callback>> snapshotCallback;
+ std::unique_ptr<mbgl::MapSnapshotter> snapshotter;
+};
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp
index a00f668c24..375d1a33aa 100644
--- a/platform/android/src/style/conversion/types.hpp
+++ b/platform/android/src/style/conversion/types.hpp
@@ -58,15 +58,15 @@ struct Converter<jni::jobject*, mbgl::style::IconTextFitType> {
};
template <>
-struct Converter<jni::jobject*, mbgl::style::TextJustifyType> {
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& value) const {
+struct Converter<jni::jobject*, mbgl::style::SymbolAnchorType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SymbolAnchorType& value) const {
return convert<jni::jobject*, std::string>(env, toString(value));
}
};
template <>
-struct Converter<jni::jobject*, mbgl::style::TextAnchorType> {
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextAnchorType& value) const {
+struct Converter<jni::jobject*, mbgl::style::TextJustifyType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& value) const {
return convert<jni::jobject*, std::string>(env, toString(value));
}
};
diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp
index e96de3b01e..a19ca33a2f 100644
--- a/platform/android/src/style/conversion/types_string_values.hpp
+++ b/platform/android/src/style/conversion/types_string_values.hpp
@@ -109,51 +109,34 @@ namespace conversion {
}
}
- // text-justify
- inline std::string toString(mbgl::style::TextJustifyType value) {
- switch (value) {
- case mbgl::style::TextJustifyType::Left:
- return "left";
- break;
- case mbgl::style::TextJustifyType::Center:
- return "center";
- break;
- case mbgl::style::TextJustifyType::Right:
- return "right";
- break;
- default:
- throw std::runtime_error("Not implemented");
- }
- }
-
- // text-anchor
- inline std::string toString(mbgl::style::TextAnchorType value) {
+ // icon-anchor
+ inline std::string toString(mbgl::style::SymbolAnchorType value) {
switch (value) {
- case mbgl::style::TextAnchorType::Center:
+ case mbgl::style::SymbolAnchorType::Center:
return "center";
break;
- case mbgl::style::TextAnchorType::Left:
+ case mbgl::style::SymbolAnchorType::Left:
return "left";
break;
- case mbgl::style::TextAnchorType::Right:
+ case mbgl::style::SymbolAnchorType::Right:
return "right";
break;
- case mbgl::style::TextAnchorType::Top:
+ case mbgl::style::SymbolAnchorType::Top:
return "top";
break;
- case mbgl::style::TextAnchorType::Bottom:
+ case mbgl::style::SymbolAnchorType::Bottom:
return "bottom";
break;
- case mbgl::style::TextAnchorType::TopLeft:
+ case mbgl::style::SymbolAnchorType::TopLeft:
return "top-left";
break;
- case mbgl::style::TextAnchorType::TopRight:
+ case mbgl::style::SymbolAnchorType::TopRight:
return "top-right";
break;
- case mbgl::style::TextAnchorType::BottomLeft:
+ case mbgl::style::SymbolAnchorType::BottomLeft:
return "bottom-left";
break;
- case mbgl::style::TextAnchorType::BottomRight:
+ case mbgl::style::SymbolAnchorType::BottomRight:
return "bottom-right";
break;
default:
@@ -161,6 +144,23 @@ namespace conversion {
}
}
+ // text-justify
+ inline std::string toString(mbgl::style::TextJustifyType value) {
+ switch (value) {
+ case mbgl::style::TextJustifyType::Left:
+ return "left";
+ break;
+ case mbgl::style::TextJustifyType::Center:
+ return "center";
+ break;
+ case mbgl::style::TextJustifyType::Right:
+ return "right";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
// text-transform
inline std::string toString(mbgl::style::TextTransformType value) {
switch (value) {
diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp
index e6321a8efa..51a48520bf 100644
--- a/platform/android/src/style/layers/custom_layer.cpp
+++ b/platform/android/src/style/layers/custom_layer.cpp
@@ -7,11 +7,12 @@
namespace mbgl {
namespace android {
- CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong deinitializeFunction, jni::jlong context)
+ CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong contextLostFunction, jni::jlong deinitializeFunction, jni::jlong context)
: Layer(env, std::make_unique<mbgl::style::CustomLayer>(
jni::Make<std::string>(env, layerId),
reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initializeFunction),
reinterpret_cast<mbgl::style::CustomLayerRenderFunction>(renderFunction),
+ reinterpret_cast<mbgl::style::CustomLayerContextLostFunction>(contextLostFunction),
reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitializeFunction),
reinterpret_cast<void*>(context))
) {
@@ -52,7 +53,7 @@ namespace android {
// Register the peer
jni::RegisterNativePeer<CustomLayer>(
env, CustomLayer::javaClass, "nativePtr",
- std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong>,
+ std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong>,
"initialize",
"finalize",
METHOD(&CustomLayer::update, "nativeUpdate"));
diff --git a/platform/android/src/style/layers/custom_layer.hpp b/platform/android/src/style/layers/custom_layer.hpp
index 3e3f3bf77f..9e079c1288 100644
--- a/platform/android/src/style/layers/custom_layer.hpp
+++ b/platform/android/src/style/layers/custom_layer.hpp
@@ -16,7 +16,7 @@ public:
static void registerNative(jni::JNIEnv&);
- CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong);
+ CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong);
CustomLayer(mbgl::Map&, mbgl::style::CustomLayer&);
diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp
index b6cf51ec7e..d44744a6cf 100644
--- a/platform/android/src/style/layers/symbol_layer.cpp
+++ b/platform/android/src/style/layers/symbol_layer.cpp
@@ -125,6 +125,12 @@ namespace android {
return jni::Object<jni::ObjectTag>(*converted);
}
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
jni::Object<jni::ObjectTag> SymbolLayer::getIconPitchAlignment(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconPitchAlignment());
@@ -520,6 +526,7 @@ namespace android {
METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"),
METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"),
METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"),
+ METHOD(&SymbolLayer::getIconAnchor, "nativeGetIconAnchor"),
METHOD(&SymbolLayer::getIconPitchAlignment, "nativeGetIconPitchAlignment"),
METHOD(&SymbolLayer::getTextPitchAlignment, "nativeGetTextPitchAlignment"),
METHOD(&SymbolLayer::getTextRotationAlignment, "nativeGetTextRotationAlignment"),
diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp
index 6d3da13ae9..417e5e143f 100644
--- a/platform/android/src/style/layers/symbol_layer.hpp
+++ b/platform/android/src/style/layers/symbol_layer.hpp
@@ -59,6 +59,8 @@ public:
jni::Object<jni::ObjectTag> getIconOffset(jni::JNIEnv&);
+ jni::Object<jni::ObjectTag> getIconAnchor(jni::JNIEnv&);
+
jni::Object<jni::ObjectTag> getIconPitchAlignment(jni::JNIEnv&);
jni::Object<jni::ObjectTag> getTextPitchAlignment(jni::JNIEnv&);
diff --git a/platform/android/tests/docs/UI_TESTS.md b/platform/android/tests/docs/UI_TESTS.md
index 6d8541a406..1014b56845 100644
--- a/platform/android/tests/docs/UI_TESTS.md
+++ b/platform/android/tests/docs/UI_TESTS.md
@@ -61,7 +61,7 @@ You can generate JaCoCo reports from espresso tests by
## Running Espresso test automatically on AWS Device Farm
To run tests on AWS device farm you need to execute `./gradlew -Pmapbox.abis=none devicefarmUpload`.
You can configure the different steps in the testapp `build.gradle`.
-AWS credentials are found in bitrise.
+AWS credentials are found in CircleCI.
diff --git a/platform/android/tests/docs/UNIT_TESTS.md b/platform/android/tests/docs/UNIT_TESTS.md
index fefb435684..458e8869f3 100644
--- a/platform/android/tests/docs/UNIT_TESTS.md
+++ b/platform/android/tests/docs/UNIT_TESTS.md
@@ -77,7 +77,7 @@ The Unit tests are executed as part of the build process on our CI and are
automatically run for each new commit pushed to this repo. If a Unit tests
fails, this will fail and stop the build.
-You can find this gradle command in our [buildscript](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/bitrise.yml#L48):
+You can find this gradle command in our [buildscript](https://github.com/mapbox/mapbox-gl-native/blob/master/circle.yml#L146-L215):
```
$ ./gradlew -Pmapbox.abis=none testReleaseUnitTest
diff --git a/platform/darwin/docs/guides/Working with GeoJSON Data.md b/platform/darwin/docs/guides/Working with GeoJSON Data.md
index 57aaa3855d..f3b3dc0918 100644
--- a/platform/darwin/docs/guides/Working with GeoJSON Data.md
+++ b/platform/darwin/docs/guides/Working with GeoJSON Data.md
@@ -81,8 +81,10 @@ Linear ring | `MGLPolygon.coordinates`, `MGLPolygon.interiorPolygons`
A `Feature` object in GeoJSON corresponds to an instance of an `MGLShape`
subclass conforming to the `MGLFeature` protocol. There is a distinct
`MGLFeature`-conforming class for each type of geometry that a GeoJSON feature
-can contain. This allows features to be used as shapes where convenient. For
-example, some features can be added to a map view as annotations.
+can contain. This allows features to be used as raw shapes where convenient. For
+example, some features can be added to a map view as annotations. Note that
+identifiers and attributes will not be available for feature querying when a
+feature is used as an annotation.
In contrast to the GeoJSON standard, it is possible for `MGLShape` subclasses
other than `MGLPointAnnotation` to straddle the antimeridian.
diff --git a/platform/darwin/resources/ca.lproj/Foundation.strings b/platform/darwin/resources/ca.lproj/Foundation.strings
index e36ea9ed8a..f86442f26f 100644
--- a/platform/darwin/resources/ca.lproj/Foundation.strings
+++ b/platform/darwin/resources/ca.lproj/Foundation.strings
@@ -1,8 +1,8 @@
/* Clock position format, long: {hours} o’clock */
-"CLOCK_FMT_LONG" = "%@ en punt";
+"CLOCK_FMT_LONG" = "a les %@";
/* Clock position format, medium: {hours} o’clock */
-"CLOCK_FMT_MEDIUM" = "%@ en punt";
+"CLOCK_FMT_MEDIUM" = "a les %@";
/* Clock position format, short: {hours}:00 */
"CLOCK_FMT_SHORT" = "%@:00";
diff --git a/platform/darwin/resources/de.lproj/Foundation.stringsdict b/platform/darwin/resources/de.lproj/Foundation.stringsdict
index 776528a99c..fb9073fa96 100644
--- a/platform/darwin/resources/de.lproj/Foundation.stringsdict
+++ b/platform/darwin/resources/de.lproj/Foundation.stringsdict
@@ -2,6 +2,22 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>COORD_DEG_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@degrees@</string>
+ <key>degrees</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d Grad</string>
+ <key>other</key>
+ <string>%d Grad</string>
+ </dict>
+ </dict>
<key>COORD_MIN_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
diff --git a/platform/darwin/resources/fr.lproj/Foundation.strings b/platform/darwin/resources/fr.lproj/Foundation.strings
new file mode 100644
index 0000000000..d2f6c1f6df
--- /dev/null
+++ b/platform/darwin/resources/fr.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "%@ heures";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "%@ heures";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@h 00";
+
+/* East, long */
+"COMPASS_E_LONG" = "est";
+
+/* East, short */
+"COMPASS_E_SHORT" = "E";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "est par nord";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "EpN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "est par sud";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "EpS";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "est-nord-est";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "ENE";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "est-sud-est";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "ESE";
+
+/* North, long */
+"COMPASS_N_LONG" = "nord";
+
+/* North, short */
+"COMPASS_N_SHORT" = "N";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "nord par est";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "NpE";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "nord par ouest";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "NpO";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "nord-est";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "NE";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "nord-est par est";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "NEpE";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "nord-est par nord";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NEpN";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "nord-nord-est";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "NNE";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "nord-nord-ouest";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "NNO";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "nord-ouest";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "NO";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "nord-ouest par nord";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "NOpN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "nord-ouest par ouest";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "NOpO";
+
+/* South, long */
+"COMPASS_S_LONG" = "sud";
+
+/* South, short */
+"COMPASS_S_SHORT" = "S";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "sud par est";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "SpE";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "sud par ouest";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "SpO";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "sud-est";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "SE";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "sud-est par est";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "SEpE";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "sud-est par sud";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "SEpS";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "sud-sud-est";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "SSE";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "sud-sud-ouest";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "SSO";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "sud-ouest";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "SO";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "sud-ouest par sud";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "SOpS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "sud-ouest par ouest";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "SOpO";
+
+/* West, long */
+"COMPASS_W_LONG" = "ouest";
+
+/* West, short */
+"COMPASS_W_SHORT" = "O";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "ouest par nord";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "OpN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "ouest par sud";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "OpS";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "ouest-nord-ouest";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "ONO";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "ouest-sud-ouest";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "OSO";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d degré(s)";
+
+/* Degrees format, medium: {degrees} */
+"COORD_DEG_MEDIUM" = "%d°";
+
+/* Degrees format, short: {degrees} */
+"COORD_DEG_SHORT" = "%d°";
+
+/* Coordinate format, long: {degrees}{minutes} */
+"COORD_DM_LONG" = "%1$@ et %2$@";
+
+/* Coordinate format, medium: {degrees}{minutes} */
+"COORD_DM_MEDIUM" = "%1$@%2$@";
+
+/* Coordinate format, short: {degrees}{minutes} */
+"COORD_DM_SHORT" = "%1$@%2$@";
+
+/* Coordinate format, long: {degrees}{minutes}{seconds} */
+"COORD_DMS_LONG" = "%1$@, %2$@ et %3$@";
+
+/* Coordinate format, medium: {degrees}{minutes}{seconds} */
+"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
+
+/* Coordinate format, short: {degrees}{minutes}{seconds} */
+"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
+
+/* East longitude format, long: {longitude} */
+"COORD_E_LONG" = "%@ est";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ est";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@E";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@, %2$@";
+
+/* Coordinate pair format, medium: {latitude}, {longitude} */
+"COORD_FMT_MEDIUM" = "%1$@, %2$@";
+
+/* Coordinate pair format, short: {latitude}, {longitude} */
+"COORD_FMT_SHORT" = "%1$@, %2$@";
+
+/* Minutes format, long */
+"COORD_MIN_LONG" = "%d minute(s)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@ nord";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@ nord";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@N";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@ sud";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ sud";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d seconde(s)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ ouest";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ ouest";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@O";
+
diff --git a/platform/darwin/resources/lt.lproj/Foundation.stringsdict b/platform/darwin/resources/lt.lproj/Foundation.stringsdict
index e3bb31838a..4ab9d4a8bf 100644
--- a/platform/darwin/resources/lt.lproj/Foundation.stringsdict
+++ b/platform/darwin/resources/lt.lproj/Foundation.stringsdict
@@ -16,8 +16,6 @@
<string>%d laipsniai</string>
<key>few</key>
<string>%d laipsniai</string>
- <key>many</key>
- <string>%d laipsniai</string>
<key>other</key>
<string>%d laipsnių</string>
</dict>
@@ -36,8 +34,6 @@
<string>%d minutė</string>
<key>few</key>
<string>%d minutės</string>
- <key>many</key>
- <string>%d minutės</string>
<key>other</key>
<string>%d minučių</string>
</dict>
@@ -56,8 +52,6 @@
<string>%d sekundė</string>
<key>few</key>
<string>%d sekundės</string>
- <key>many</key>
- <string>%d sekundės</string>
<key>other</key>
<string>%d sekundžių</string>
</dict>
diff --git a/platform/darwin/resources/ru.lproj/Foundation.strings b/platform/darwin/resources/ru.lproj/Foundation.strings
new file mode 100644
index 0000000000..fd9eb04ada
--- /dev/null
+++ b/platform/darwin/resources/ru.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "%@ часов";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "%@ час.";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@:00";
+
+/* East, long */
+"COMPASS_E_LONG" = "восток";
+
+/* East, short */
+"COMPASS_E_SHORT" = "В";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "северо-восток";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "СВ";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "юго-восток";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "ЮВ";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "восток-северо-восток";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "ВСВ";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "юго-юго-восток";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "ЮЮВ";
+
+/* North, long */
+"COMPASS_N_LONG" = "север";
+
+/* North, short */
+"COMPASS_N_SHORT" = "С";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "северо-восток";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "СВ";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "северо-запад";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "СЗ";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "северо-восток";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "СВ";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "восток-северо-восток";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "ВСВ";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "северо-северо-восток";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "ССВ";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "северо-северо-восток";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "ССВ";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "северо-северо-запад";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "ССЗ";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "северо-запад";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "СЗ";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "северо-северо-запад";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "ССЗ";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "запад-северо-запад";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "ЗСЗ";
+
+/* South, long */
+"COMPASS_S_LONG" = "юг";
+
+/* South, short */
+"COMPASS_S_SHORT" = "Ю";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "юго-восток";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "ЮВ";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "юго-запад";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "ЮЗ";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "юго-восток";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "ЮВ";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "восток-юго-восток";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "ВЮВ";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "юго-юго-восток";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "ЮЮВ";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "юго-юго-восток";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "ЮЮВ";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "юго-юго-запад";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "ЮЮЗ";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "юго-запад";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "ЮЗ";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "юго-юго-запад";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "ЮЮЗ";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "восток-юго-восток";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "ВЮВ";
+
+/* West, long */
+"COMPASS_W_LONG" = "запад";
+
+/* West, short */
+"COMPASS_W_SHORT" = "З";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "северо-запад";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "СЗ";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "юго-запад";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "ЮГ";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "запад-северо-запад";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "ЗСЗ";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "запад-юго-запад";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "ЗЮЗ";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d градус(ов)";
+
+/* Degrees format, medium: {degrees} */
+"COORD_DEG_MEDIUM" = "%d°";
+
+/* Degrees format, short: {degrees} */
+"COORD_DEG_SHORT" = "%d°";
+
+/* Coordinate format, long: {degrees}{minutes} */
+"COORD_DM_LONG" = "%1$@ и %2$@";
+
+/* Coordinate format, medium: {degrees}{minutes} */
+"COORD_DM_MEDIUM" = "%1$@%2$@";
+
+/* Coordinate format, short: {degrees}{minutes} */
+"COORD_DM_SHORT" = "%1$@%2$@";
+
+/* Coordinate format, long: {degrees}{minutes}{seconds} */
+"COORD_DMS_LONG" = "%1$@, %2$@, и %3$@";
+
+/* Coordinate format, medium: {degrees}{minutes}{seconds} */
+"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
+
+/* Coordinate format, short: {degrees}{minutes}{seconds} */
+"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
+
+/* East longitude format, long: {longitude} */
+"COORD_E_LONG" = "%@восточной долготы";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@восточной долготы";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@E";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@,%2$@";
+
+/* Coordinate pair format, medium: {latitude}, {longitude} */
+"COORD_FMT_MEDIUM" = "%1$@, %2$@";
+
+/* Coordinate pair format, short: {latitude}, {longitude} */
+"COORD_FMT_SHORT" = "%1$@, %2$@";
+
+/* Minutes format, long */
+"COORD_MIN_LONG" = "%d минут(а)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@северной широты";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@северной широты";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@N";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@южной широты";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@южной широты";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d секунд(а)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@западной долготы";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@западной долготы";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@W";
+
diff --git a/platform/darwin/resources/uk.lproj/Foundation.strings b/platform/darwin/resources/uk.lproj/Foundation.strings
new file mode 100644
index 0000000000..32a006829f
--- /dev/null
+++ b/platform/darwin/resources/uk.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "%@год.";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "%@г.";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@:00";
+
+/* East, long */
+"COMPASS_E_LONG" = "схід";
+
+/* East, short */
+"COMPASS_E_SHORT" = "Сх";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "північний схід";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "ПнСх";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "південний схід";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "ПдСх";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "схід - північний схід";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "Сх-ПнСх";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "схід - південний схід";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "Сх-ПдСх";
+
+/* North, long */
+"COMPASS_N_LONG" = "північ";
+
+/* North, short */
+"COMPASS_N_SHORT" = "Пн";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "північний схід";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "ПнСх";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "північний захід";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "ПнЗх";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "північний схід";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "ПнСх";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "північний схід - схід";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "ПнСх-Сх";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "північний счхід - північ";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "ПнСх-Пн";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "північ - північний схід";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "Пн-ПнСх";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "північ - північний захід";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "Пн-ПнЗх";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "північний захід";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "ПнЗх";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "північний захід - північ";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "ПнЗх-Пн";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "північний захід - захід";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "ПнЗх-Зх";
+
+/* South, long */
+"COMPASS_S_LONG" = "південь";
+
+/* South, short */
+"COMPASS_S_SHORT" = "Пд";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "південний схід";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "ПдСх";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "південний захід";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "ПдЗх";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "південний схід";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "ПдСх";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "південний схід - схід";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "ПдСх-Сх";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "південний схід - південь";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "ПдСх-Пд";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "південь - південний схід";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "Пд-ПдСх";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "південь - південний захід";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "Пд-ПдЗх";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "південний захід";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "ПдЗх";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "південний захід - південь";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "ПдЗх-Пд";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "південний захід - захід";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "ПдЗх-Зх";
+
+/* West, long */
+"COMPASS_W_LONG" = "захід";
+
+/* West, short */
+"COMPASS_W_SHORT" = "Зх";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "північний захід";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "ПнЗх";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "південний захід";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "ПдЗх";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "захід - північний захід";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "Зх-ПнЗх";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "захід - південний захід";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "Зх-ПдЗх";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d градус(ів)";
+
+/* Degrees format, medium: {degrees} */
+"COORD_DEG_MEDIUM" = "%d°";
+
+/* Degrees format, short: {degrees} */
+"COORD_DEG_SHORT" = "%d°";
+
+/* Coordinate format, long: {degrees}{minutes} */
+"COORD_DM_LONG" = "%1$@ та %2$@";
+
+/* Coordinate format, medium: {degrees}{minutes} */
+"COORD_DM_MEDIUM" = "%1$@%2$@";
+
+/* Coordinate format, short: {degrees}{minutes} */
+"COORD_DM_SHORT" = "%1$@%2$@";
+
+/* Coordinate format, long: {degrees}{minutes}{seconds} */
+"COORD_DMS_LONG" = "%1$@, %2$@, та %3$@";
+
+/* Coordinate format, medium: {degrees}{minutes}{seconds} */
+"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
+
+/* Coordinate format, short: {degrees}{minutes}{seconds} */
+"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
+
+/* East longitude format, long: {longitude} */
+"COORD_E_LONG" = "%@ східної довготи";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ східної довготи";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@Сх";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@, %2$@";
+
+/* Coordinate pair format, medium: {latitude}, {longitude} */
+"COORD_FMT_MEDIUM" = "%1$@, %2$@";
+
+/* Coordinate pair format, short: {latitude}, {longitude} */
+"COORD_FMT_SHORT" = "%1$@, %2$@";
+
+/* Minutes format, long */
+"COORD_MIN_LONG" = "%d хвилин(а)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@ північної широти";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@ північної широти";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@Пн";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@ південної широти";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ південної широти";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@Пд";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d секунд(а)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ західної довготи";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ західної довготи";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@Зх";
+
diff --git a/platform/darwin/resources/uk.lproj/Foundation.stringsdict b/platform/darwin/resources/uk.lproj/Foundation.stringsdict
index f35f0516e1..fdad193d4d 100644
--- a/platform/darwin/resources/uk.lproj/Foundation.stringsdict
+++ b/platform/darwin/resources/uk.lproj/Foundation.stringsdict
@@ -16,10 +16,8 @@
<string>%d градус</string>
<key>few</key>
<string>%d градуси</string>
- <key>many</key>
- <string>%d градусів</string>
<key>other</key>
- <string>%d градуса</string>
+ <string>%d градусів</string>
</dict>
</dict>
<key>COORD_MIN_LONG</key>
@@ -33,13 +31,11 @@
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
- <string>%d мінута</string>
+ <string>%d хвилина</string>
<key>few</key>
- <string>%d мінути</string>
- <key>many</key>
- <string>%d мінут</string>
+ <string>%d хвилини</string>
<key>other</key>
- <string>%d мінути</string>
+ <string>%d хвилин</string>
</dict>
</dict>
<key>COORD_SEC_LONG</key>
@@ -56,10 +52,8 @@
<string>%d секунда</string>
<key>few</key>
<string>%d секунди</string>
- <key>many</key>
- <string>%d секунд</string>
<key>other</key>
- <string>%d секунди</string>
+ <string>%d секунд</string>
</dict>
</dict>
</dict>
diff --git a/platform/darwin/resources/vi.lproj/Foundation.stringsdict b/platform/darwin/resources/vi.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..d85707f7ff
--- /dev/null
+++ b/platform/darwin/resources/vi.lproj/Foundation.stringsdict
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>COORD_DEG_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@degrees@</string>
+ <key>degrees</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>other</key>
+ <string>%d độ</string>
+ </dict>
+ </dict>
+ <key>COORD_MIN_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@minutes@</string>
+ <key>minutes</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>other</key>
+ <string>%d phút</string>
+ </dict>
+ </dict>
+ <key>COORD_SEC_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@seconds@</string>
+ <key>seconds</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>other</key>
+ <string>%d giây</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/darwin/resources/zh-Hant.lproj/Foundation.strings b/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
index 41b1d2baae..8a02bcfac0 100644
--- a/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
+++ b/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
@@ -1,4 +1,4 @@
-/* Clock position format, long: {hours} o’clock */
+/* Clock position format, long: {hours} o’clock */
"CLOCK_FMT_LONG" = "%@點";
/* Clock position format, medium: {hours} o’clock */
@@ -26,16 +26,16 @@
"COMPASS_EbS_SHORT" = "東微南";
/* East-northeast, long */
-"COMPASS_ENE_LONG" = "東北偏東";
+"COMPASS_ENE_LONG" = "東北東";
/* East-northeast, short */
-"COMPASS_ENE_SHORT" = "東北偏東";
+"COMPASS_ENE_SHORT" = "東北東";
/* East-southeast, long */
-"COMPASS_ESE_LONG" = "東南偏東";
+"COMPASS_ESE_LONG" = "東南東";
/* East-southeast, short */
-"COMPASS_ESE_SHORT" = "東南偏東";
+"COMPASS_ESE_SHORT" = "東南東";
/* North, long */
"COMPASS_N_LONG" = "北";
@@ -74,16 +74,16 @@
"COMPASS_NEbN_SHORT" = "東北微北";
/* North-northeast, long */
-"COMPASS_NNE_LONG" = "東北偏北";
+"COMPASS_NNE_LONG" = "北北東";
/* North-northeast, short */
-"COMPASS_NNE_SHORT" = "東北偏北";
+"COMPASS_NNE_SHORT" = "北北東";
/* North-northwest, long */
-"COMPASS_NNW_LONG" = "西北偏北";
+"COMPASS_NNW_LONG" = "北北西";
/* North-northwest, short */
-"COMPASS_NNW_SHORT" = "西北偏北";
+"COMPASS_NNW_SHORT" = "北北西";
/* Northwest, long */
"COMPASS_NW_LONG" = "西北";
@@ -140,16 +140,16 @@
"COMPASS_SEbS_SHORT" = "東南微南";
/* South-southeast, long */
-"COMPASS_SSE_LONG" = "東南偏南";
+"COMPASS_SSE_LONG" = "南南東";
/* South-southeast, short */
-"COMPASS_SSE_SHORT" = "東南偏南";
+"COMPASS_SSE_SHORT" = "南南東";
/* South-southwest, long */
-"COMPASS_SSW_LONG" = "西南偏南";
+"COMPASS_SSW_LONG" = "南南西";
/* South-southwest, short */
-"COMPASS_SSW_SHORT" = "西南偏南";
+"COMPASS_SSW_SHORT" = "南南西";
/* Southwest, long */
"COMPASS_SW_LONG" = "西南";
@@ -158,16 +158,16 @@
"COMPASS_SW_SHORT" = "西南";
/* Southwest by south, long */
-"COMPASS_SWbS_LONG" = "西南偏南";
+"COMPASS_SWbS_LONG" = "西南微南";
/* Southwest by south, short */
-"COMPASS_SWbS_SHORT" = "西南偏南";
+"COMPASS_SWbS_SHORT" = "西南微南";
/* Southwest by west, long */
-"COMPASS_SWbW_LONG" = "西南偏西";
+"COMPASS_SWbW_LONG" = "西南微西";
/* Southwest by west, short */
-"COMPASS_SWbW_SHORT" = "西南偏西";
+"COMPASS_SWbW_SHORT" = "西南微西";
/* West, long */
"COMPASS_W_LONG" = "西";
@@ -188,16 +188,16 @@
"COMPASS_WbS_SHORT" = "西微南";
/* West-northwest, long */
-"COMPASS_WNW_LONG" = "西北偏西";
+"COMPASS_WNW_LONG" = "西北西";
/* West-northwest, short */
-"COMPASS_WNW_SHORT" = "西北偏西";
+"COMPASS_WNW_SHORT" = "西北西";
/* West-southwest, long */
-"COMPASS_WSW_LONG" = "西南偏西";
+"COMPASS_WSW_LONG" = "西南西";
/* West-southwest, short */
-"COMPASS_WSW_SHORT" = "西南偏西";
+"COMPASS_WSW_SHORT" = "西南西";
/* Degrees format, long */
"COORD_DEG_LONG" = "%d度";
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 5a4936c0ee..4ee86e65da 100644
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -148,6 +148,9 @@ global.mbglTestValue = function (property, layerType) {
if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) {
type = 'Alignment';
}
+ if (/^(text|icon)-anchor$/.test(originalPropertyName(property))) {
+ type = 'SymbolAnchor'
+ }
let value = camelize(_.last(_.keys(property.values)));
if (property['light-property']) {
return `mbgl::style::Light${type}Type::${value}`;
@@ -500,6 +503,9 @@ global.mbglType = function(property) {
if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) {
type = 'Alignment';
}
+ if (/^(text|icon)-anchor$/.test(originalPropertyName(property))) {
+ type = 'SymbolAnchor'
+ }
return `mbgl::style::${type}Type`;
}
case 'color':
@@ -648,10 +654,10 @@ while ((match = exampleRegex.exec(examplesSrc)) !== null) {
let testMethodName = match[1],
indentation = match[2],
exampleCode = match[3];
-
+
// Trim leading whitespace from the example code.
exampleCode = exampleCode.replace(new RegExp('^' + indentation, 'gm'), '');
-
+
examples[testMethodName] = exampleCode;
}
@@ -663,13 +669,13 @@ global.guideExample = function (guide, exampleId, os) {
console.error(`MGLDocumentationExampleTests.test${testMethodName}() not found.`);
process.exit(1);
}
-
+
// Resolve conditional compilation blocks.
example = example.replace(/^(\s*)#if\s+os\((iOS|macOS)\)\n([^]*?)(?:^\1#else\n([^]*?))?^\1#endif\b\n?/gm,
function (m, indentation, ifOs, ifCase, elseCase) {
return (os === ifOs ? ifCase : elseCase).replace(new RegExp('^ ', 'gm'), '');
}).replace(/\n$/, '');
-
+
return '```swift\n' + example + '\n```';
};
diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h
index 491c89b608..a13821cf96 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -18,15 +18,23 @@ NS_ASSUME_NONNULL_BEGIN
You can add custom data to display on the map by creating feature objects and
adding them to an `MGLShapeSource` using the
`-[MGLShapeSource initWithIdentifier:shape:options:]` method or
- `MGLShapeSource.shape` property. Similarly, you can add `MGLPointFeature`,
- `MGLPolylineFeature`, and `MGLPolygonFeature` objects to the map as annotations
- using `-[MGLMapView addAnnotations:]` and related methods.
+ `MGLShapeSource.shape` property.
In addition to adding data to the map, you can also extract data from the map:
`-[MGLMapView visibleFeaturesAtPoint:]` and related methods return feature
objects that correspond to features in the source. This enables you to inspect
the properties of features in vector tiles loaded by `MGLVectorSource` objects.
You also reuse these feature objects as overlay annotations.
+
+ While it is possible to add `MGLFeature`-conforming objects to the map as
+ annotations using `-[MGLMapView addAnnotations:]` and related methods, doing so
+ has trade-offs:
+
+ - Features added as annotations will not have `identifier` or `attributes`
+ properties when used with feature querying.
+
+ - Features added as annotations become interactive. Taps and selection can be
+ handled in `-[MGLMapViewDelegate mapView:didSelectAnnotation:]`.
*/
@protocol MGLFeature <MGLAnnotation>
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index e169ee19bb..84f1a1ff25 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -42,6 +42,15 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, coordinate = %f, %f, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ self.coordinate.latitude, self.coordinate.longitude,
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLPolylineFeature ()
@@ -68,6 +77,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)[self pointCount],
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLPolygonFeature ()
@@ -94,6 +113,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)[self pointCount],
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLPointCollectionFeature ()
@@ -146,6 +175,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)self.polylines.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLMultiPolygonFeature ()
@@ -172,6 +211,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)self.polygons.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLShapeCollectionFeature ()
diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h
new file mode 100644
index 0000000000..615d39bee4
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -0,0 +1,148 @@
+#import <Foundation/Foundation.h>
+#import "MGLTypes.h"
+#import "MGLGeometry.h"
+#import "MGLMapCamera.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+MGL_EXPORT
+/**
+ The options to use when creating images with the `MGLMapsnapshotter`.
+ */
+@interface MGLMapSnapshotOptions : NSObject
+
+/**
+ Creates a set of options with the minimum required information.
+
+ @param styleURL URL of the map style to snapshot. The URL may be a full HTTP or HTTPS URL,
+ a Mapbox URL indicating the style’s map ID (`mapbox://styles/{user}/{style`}), or a path
+ to a local file relative to the application’s resource path. Specify `nil` for the default style.
+ @param size The image size.
+ */
+- (instancetype)initWithStyleURL:(nullable NSURL *)styleURL camera:(MGLMapCamera *)camera size:(CGSize)size;
+
+#pragma mark - Configuring the Map
+
+/**
+ URL of the map style to snapshot.
+ */
+@property (nonatomic, readonly) NSURL *styleURL;
+
+/**
+ The zoom level.
+
+ The default zoom level is 0. If this property is non-zero and the camera property
+ is non-nil, the camera’s altitude is ignored in favor of this property’s value.
+ */
+@property (nonatomic) double zoomLevel;
+
+/**
+ A camera representing the viewport visible in the snapshot.
+
+ If this property is non-nil and the `coordinateBounds` property is set to a non-empty
+ coordinate bounds, the camera’s center coordinate and altitude are ignored in favor
+ of the `coordinateBounds` property.
+ */
+@property (nonatomic) MGLMapCamera *camera;
+
+/**
+ The cooordinate rectangle that encompasses the bounds to capture.
+
+ If this property is non-empty and the camera property is non-nil, the camera’s
+ center coordinate and altitude are ignored in favor of this property’s value.
+ */
+@property (nonatomic) MGLCoordinateBounds coordinateBounds;
+
+#pragma mark - Configuring the Image
+
+/**
+ The size of the output image, measured in points.
+
+ */
+@property (nonatomic, readonly) CGSize size;
+
+/**
+ The scale of the output image. Defaults to the main screen scale.
+ Minimum is 1.
+ */
+@property (nonatomic) CGFloat scale;
+
+@end
+
+#if TARGET_OS_IPHONE
+/**
+ A block to processes the result or error of a snapshot request.
+
+ @param snapshot The `UIImage` that was generated or `nil` if an error occurred.
+ @param error The error that occured or `nil` when successful.
+ */
+typedef void (^MGLMapSnapshotCompletionHandler)(UIImage* _Nullable snapshot, NSError* _Nullable error);
+#else
+/**
+ A block to processes the result or error of a snapshot request.
+
+ @param snapshot The `NSImage` that was generated or `nil` if an error occurred.
+ @param error The eror that occured or `nil` when succesful.
+ */
+typedef void (^MGLMapSnapshotCompletionHandler)(NSImage* _Nullable snapshot, NSError* _Nullable error);
+#endif
+
+/**
+ An immutable utility object for capturing map-based images.
+
+ ### Example
+
+ ```swift
+ var camera = MGLMapCamera()
+ camera.centerCoordinate = CLLocationCoordinate2D(latitude: 37.7184, longitude: -122.4365)
+ camera.pitch = 20
+
+ var options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL(), camera: camera, size: CGSize(width: 320, height: 480))
+ options.zoomLevel = 10
+
+ var snapshotter = MGLMapSnapshotter(options: options)
+ snapshotter.start { (image, error) in
+ if error {
+ // error handler
+ } else {
+ // image handler
+ }
+ }
+ ```
+ */
+MGL_EXPORT
+@interface MGLMapSnapshotter : NSObject
+
+- (instancetype)initWithOptions:(MGLMapSnapshotOptions*)options;
+
+/**
+ Starts the snapshot creation and executes the specified block with the result.
+
+ @param completionHandler The block to handle the result in.
+ */
+- (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
+
+/**
+ Starts the snapshot creation and executes the specified block with the result on the specified queue.
+
+ @param queue The queue to handle the result on.
+ @param completionHandler The block to handle the result in.
+ */
+- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
+
+/**
+ Cancels the snapshot creation request, if any.
+
+ Once you call this method, you cannot resume the snapshot. In order to obtain the
+ snapshot, create a new `MGLMapSnapshotter` object.
+ */
+- (void)cancel;
+
+/**
+ Indicates whether as snapshot is currently being generated.
+ */
+@property (nonatomic, readonly, getter=isLoading) BOOL loading;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
new file mode 100644
index 0000000000..835e1995f3
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -0,0 +1,189 @@
+#import "MGLMapSnapshotter.h"
+
+#import <mbgl/actor/actor.hpp>
+#import <mbgl/actor/scheduler.hpp>
+#import <mbgl/util/geo.hpp>
+#import <mbgl/map/map_snapshotter.hpp>
+#import <mbgl/map/camera.hpp>
+#import <mbgl/storage/default_file_source.hpp>
+#import <mbgl/util/default_thread_pool.hpp>
+#import <mbgl/util/string.hpp>
+#import <mbgl/util/shared_thread_pool.hpp>
+
+#import "MGLOfflineStorage_Private.h"
+#import "MGLGeometry_Private.h"
+#import "NSBundle+MGLAdditions.h"
+#import "MGLStyle.h"
+
+#if TARGET_OS_IPHONE
+#import "UIImage+MGLAdditions.h"
+#else
+#import "NSImage+MGLAdditions.h"
+#endif
+
+const CGPoint MGLLogoImagePosition = CGPointMake(8, 8);
+const CGFloat MGLSnapshotterMinimumPixelSize = 64;
+
+@implementation MGLMapSnapshotOptions
+
+- (instancetype _Nonnull)initWithStyleURL:(nullable NSURL*)styleURL camera:(MGLMapCamera*)camera size:(CGSize) size;
+{
+ self = [super init];
+ if (self) {
+ if ( !styleURL)
+ {
+ styleURL = [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion];
+ }
+ _styleURL = styleURL;
+ _size = size;
+ _camera = camera;
+#if TARGET_OS_IPHONE
+ _scale = [UIScreen mainScreen].scale;
+#else
+ _scale = [NSScreen mainScreen].backingScaleFactor;
+#endif
+
+ }
+ return self;
+}
+
+@end
+
+@interface MGLMapSnapshotter()
+@property (nonatomic) MGLMapSnapshotOptions *options;
+@end
+
+@implementation MGLMapSnapshotter {
+
+ std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
+ std::unique_ptr<mbgl::MapSnapshotter> _mbglMapSnapshotter;
+ std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> _snapshotCallback;
+}
+
+- (instancetype)initWithOptions:(MGLMapSnapshotOptions*)options;
+{
+ self = [super init];
+ if (self) {
+ _options = options;
+ _loading = false;
+
+ mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
+ _mbglThreadPool = mbgl::sharedThreadPool();
+
+ std::string styleURL = std::string([options.styleURL.absoluteString UTF8String]);
+
+ // Size; taking into account the minimum texture size for OpenGL ES
+ // For non retina screens the ratio is 1:1 MGLSnapshotterMinimumPixelSize
+ mbgl::Size size = {
+ static_cast<uint32_t>(MAX(options.size.width, MGLSnapshotterMinimumPixelSize)),
+ static_cast<uint32_t>(MAX(options.size.height, MGLSnapshotterMinimumPixelSize))
+ };
+
+ float pixelRatio = MAX(options.scale, 1);
+
+ // Camera options
+ mbgl::CameraOptions cameraOptions;
+ if (CLLocationCoordinate2DIsValid(options.camera.centerCoordinate)) {
+ cameraOptions.center = MGLLatLngFromLocationCoordinate2D(options.camera.centerCoordinate);
+ }
+ cameraOptions.angle = MAX(0, options.camera.heading) * mbgl::util::DEG2RAD;
+ cameraOptions.zoom = MAX(0, options.zoomLevel);
+ cameraOptions.pitch = MAX(0, options.camera.pitch);
+
+ // Region
+ mbgl::optional<mbgl::LatLngBounds> coordinateBounds;
+ if (!MGLCoordinateBoundsIsEmpty(options.coordinateBounds)) {
+ coordinateBounds = MGLLatLngBoundsFromCoordinateBounds(options.coordinateBounds);
+ }
+
+ // Create the snapshotter
+ _mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *_mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, coordinateBounds);
+ }
+ return self;
+}
+
+- (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)completion;
+{
+ [self startWithQueue:dispatch_get_main_queue() completionHandler:completion];
+}
+
+- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completion;
+{
+ if ([self isLoading]) {
+ [NSException raise:NSInternalInconsistencyException
+ format:@"Already started this snapshotter."];
+ }
+
+ _loading = true;
+
+ dispatch_async(queue, ^{
+ _snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image) {
+ _loading = false;
+ if (mbglError) {
+ NSString *description = @(mbgl::util::toString(mbglError).c_str());
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeSnapshotFailed userInfo:userInfo];
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ completion(nil, error);
+ });
+ } else {
+#if TARGET_OS_IPHONE
+ MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image) scale:self.options.scale];
+#else
+ MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image)];
+ mglImage.size = NSMakeSize(mglImage.size.width / self.options.scale,
+ mglImage.size.height / self.options.scale);
+#endif
+
+ // Process image watermark in a work queue
+ dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(workQueue, ^{
+#if TARGET_OS_IPHONE
+ UIImage *logoImage = [UIImage imageNamed:@"mapbox" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
+
+ UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, self.options.scale);
+
+ [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)];
+ [logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width,logoImage.size.height)];
+ UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
+
+ UIGraphicsEndImageContext();
+#else
+ NSImage *logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox" ofType:@"pdf"]];
+ NSImage *sourceImage = mglImage;
+
+ NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height);
+ NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height);
+ NSImage *compositedImage = nil;
+ NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame
+ context:nil
+ hints:nil];
+ compositedImage = [[NSImage alloc] initWithSize:targetSize];
+
+ [compositedImage lockFocus];
+ [sourceImageRep drawInRect: targetFrame];
+ [logoImage drawInRect:CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width,logoImage.size.height)];
+ [compositedImage unlockFocus];
+
+#endif
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ completion(compositedImage, nil);
+ });
+ });
+ }
+ });
+ _mbglMapSnapshotter->snapshot(_snapshotCallback->self());
+ });
+}
+
+- (void)cancel;
+{
+ _snapshotCallback.reset();
+ _mbglMapSnapshotter.reset();
+}
+
+@end
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 2ddfa649e9..7085aa58e5 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -11,6 +11,7 @@
#import "NSValue+MGLAdditions.h"
#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
@@ -81,7 +82,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
- (void)setDelegate:(id<MGLOfflineStorageDelegate>)newValue {
_delegate = newValue;
if ([self.delegate respondsToSelector:@selector(offlineStorage:URLForResourceOfKind:withURL:)]) {
- _mbglResourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform>>(*mbgl::util::RunLoop::Get(), [offlineStorage = self](auto kind_, const std::string&& url_) -> std::string {
+ _mbglResourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform>>(*mbgl::Scheduler::GetCurrent(), [offlineStorage = self](auto kind_, const std::string&& url_) -> std::string {
NSURL* url =
[NSURL URLWithString:[[NSString alloc] initWithBytes:url_.data()
length:url_.length()
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index d966ff13ce..e7843224e9 100644
--- a/platform/darwin/src/MGLPolygon.mm
+++ b/platform/darwin/src/MGLPolygon.mm
@@ -200,4 +200,14 @@
@"coordinates": coordinates};
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
+ self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle,
+ (unsigned long)self.polygons.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds)];
+}
+
@end
diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h
index b3db0fd39f..e46baa91cc 100644
--- a/platform/darwin/src/MGLPolyline.h
+++ b/platform/darwin/src/MGLPolyline.h
@@ -33,8 +33,18 @@ NS_ASSUME_NONNULL_BEGIN
`MGLPolygon` object. To group multiple polylines together in one shape, use an
`MGLMultiPolyline` or `MGLShapeCollection` object.
- To make the polyline straddle the antimeridian, specify some longitudes less
- than −180 degrees or greater than 180 degrees.
+ To make the polyline go across the antimeridian or international date line,
+ specify some longitudes less than −180 degrees or greater than 180 degrees.
+ For example, a polyline that stretches from Tokyo to San Francisco would have
+ coordinates of (35.68476, -220.24257) and (37.78428, -122.41310).
+
+ ```swift
+ let coordinates = [
+ CLLocationCoordinate2D(latitude: 35.68476, longitude: -220.24257),
+ CLLocationCoordinate2D(latitude: 37.78428, longitude: -122.41310)
+ ]
+ let polyline = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))
+ ```
A polyline is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.4">LineString</a>
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index fd75dc2795..0e371a4dda 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -201,4 +201,14 @@
@"coordinates": coordinates};
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
+ self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle,
+ (unsigned long)self.polylines.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds)];
+}
+
@end
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index f02fc98ded..571cbdcc62 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -98,8 +98,8 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
}
std::vector<mbgl::Feature> features;
- if (self.style) {
- features = self.style.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
+ if (self.mapView) {
+ features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
}
return MGLFeaturesFromMBGLFeatures(features);
}
diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm
index ee012f4d66..6d57e14e8c 100644
--- a/platform/darwin/src/MGLSource.mm
+++ b/platform/darwin/src/MGLSource.mm
@@ -1,7 +1,9 @@
#import "MGLSource_Private.h"
#import "MGLStyle_Private.h"
+#import "MGLMapView_Private.h"
#include <mbgl/style/style.hpp>
+#include <mbgl/map/map.hpp>
#include <mbgl/style/source.hpp>
@interface MGLSource ()
@@ -10,7 +12,7 @@
// special internal source types like mbgl::AnnotationSource.
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
-@property (nonatomic, readonly, weak) MGLStyle *style;
+@property (nonatomic, readonly, weak) MGLMapView *mapView;
@end
@@ -27,37 +29,38 @@
return self;
}
-- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource {
+- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(MGLMapView *)mapView {
NSString *identifier = @(rawSource->getID().c_str());
if (self = [self initWithIdentifier:identifier]) {
_rawSource = rawSource;
_rawSource->peer = SourceWrapper { self };
+ _mapView = mapView;
}
return self;
}
- (instancetype)initWithPendingSource:(std::unique_ptr<mbgl::style::Source>)pendingSource {
- if (self = [self initWithRawSource:pendingSource.get()]) {
+ if (self = [self initWithRawSource:pendingSource.get() mapView:nil]) {
_pendingSource = std::move(pendingSource);
}
return self;
}
-- (void)addToStyle:(MGLStyle *)style {
+- (void)addToMapView:(MGLMapView *)mapView {
if (_pendingSource == nullptr) {
[NSException raise:@"MGLRedundantSourceException"
format:@"This instance %@ was already added to %@. Adding the same source instance " \
- "to the style more than once is invalid.", self, style];
+ "to the style more than once is invalid.", self, mapView.style];
}
-
- _style = style;
- style.rawStyle->addSource(std::move(_pendingSource));
+
+ _mapView = mapView;
+ _mapView.style.rawStyle->addSource(std::move(_pendingSource));
}
-- (void)removeFromStyle:(MGLStyle *)style {
- if (self.rawSource == style.rawStyle->getSource(self.identifier.UTF8String)) {
- _pendingSource = style.rawStyle->removeSource(self.identifier.UTF8String);
- _style = nil;
+- (void)removeFromMapView:(MGLMapView *)mapView {
+ if (self.rawSource == mapView.style.rawStyle->getSource(self.identifier.UTF8String)) {
+ _pendingSource = mapView.style.rawStyle->removeSource(self.identifier.UTF8String);
+ _mapView = nil;
}
}
diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h
index ba78973279..d7d1f66641 100644
--- a/platform/darwin/src/MGLSource_Private.h
+++ b/platform/darwin/src/MGLSource_Private.h
@@ -18,7 +18,7 @@ struct SourceWrapper {
__weak MGLSource *source;
};
-@class MGLStyle;
+@class MGLMapView;
@interface MGLSource (Private)
@@ -26,7 +26,7 @@ struct SourceWrapper {
Initializes and returns a source with a raw pointer to the backing store,
associated with a style.
*/
-- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource;
+- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(nullable MGLMapView *)mapView;
/**
Initializes and returns a source with an owning pointer to the backing store,
@@ -44,33 +44,30 @@ struct SourceWrapper {
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
/**
- The style which currently contains the source.
-
- If the source is not currently part of a style, this property is
+ The map view whose style currently contains the source.
+ If the source is not currently part of any map view’s style, this property is
set to `nil`.
*/
-@property (nonatomic, readonly, weak) MGLStyle *style;
+@property (nonatomic, readonly, weak) MGLMapView *mapView;
/**
- Adds the mbgl source that this object represents to the style.
-
+ Adds the mbgl source that this object represents to the mbgl map.
Once a mbgl source is added, ownership of the object is transferred to the
- `mbgl::Style` and this object no longer has an active unique_ptr reference to the
+ `mbgl::Map` and this object no longer has an active unique_ptr reference to the
`mbgl::Source`. If this object's mbgl source is in that state, the mbgl source
can still be changed but the changes will not be visible until the `MGLSource`
- is added back to the style via `-[MGLStyle addSource:]` and styled with a
+ is added back to the map via `-[MGLStyle addSource:]` and styled with a
`MGLLayer`.
*/
-- (void)addToStyle:(MGLStyle *)style;
+- (void)addToMapView:(MGLMapView *)mapView;
/**
- Removes the mbgl source that this object represents from the style.
-
+ Removes the mbgl source that this object represents from the mbgl map.
When a mbgl source is removed, ownership of the object is transferred back
to the `MGLSource` instance and the unique_ptr reference is valid again. It is
safe to add the source back to the style after it is removed.
*/
-- (void)removeFromStyle:(MGLStyle *)style;
+- (void)removeFromMapView:(MGLMapView *)mapView;
@end
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index 6fb4a6cc6b..0b360de8fc 100644
--- a/platform/darwin/src/MGLStyle.h
+++ b/platform/darwin/src/MGLStyle.h
@@ -233,66 +233,38 @@ MGL_EXPORT
+ (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version;
/**
- Returns the URL to the current version of the
+ Returns the URL to version 2 of the
<a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Day</a>
style.
- Traffic Day color-codes roads based on live traffic congestion data. Traffic
- data is currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
- @warning The return value may change in a future release of the SDK. If you use
- any feature that depends on a specific aspect of a default style – for
- instance, the minimum zoom level that includes roads – use the
- `-trafficDayStyleURLWithVersion:` method instead. Such details may change
- significantly from version to version.
*/
-+ (NSURL *)trafficDayStyleURL;
++ (NSURL *)trafficDayStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));
/**
Returns the URL to the given version of the
<a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Day</a>
style as of publication.
-
- Traffic Day color-codes roads based on live traffic congestion data. Traffic
- data is currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
+
@param version A specific version of the style.
*/
-+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version;
++ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));;
/**
- Returns the URL to the current version of the
+ Returns the URL to the version 2 of the
<a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Night</a>
style.
- Traffic Night color-codes roads based on live traffic congestion data and is
- designed to maximize legibility in low-light situations. Traffic data is
- currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
- @warning The return value may change in a future release of the SDK. If you use
- any feature that depends on a specific aspect of a default style – for
- instance, the minimum zoom level that includes roads – use the
- `-trafficNightStyleURLWithVersion:` method instead. Such details may change
- significantly from version to version.
*/
-+ (NSURL *)trafficNightStyleURL;
++ (NSURL *)trafficNightStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
/**
- Returns the URL to the given version of the
+ Returns the URL to to the version 2 of the
<a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Night</a>
style as of publication.
-
- Traffic Night color-codes roads based on live traffic congestion data and is
- designed to maximize legibility in low-light situations. Traffic data is
- currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
+
@param version A specific version of the style.
*/
-+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version;
++ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
#pragma mark Accessing Metadata About the Style
@@ -557,6 +529,21 @@ MGL_EXPORT
*/
@property (nonatomic, strong) MGLLight *light;
+#pragma mark Localizing Map Content
+
+/**
+ A Boolean value that determines whether the style attempts to localize labels in
+ the style into the system’s preferred language.
+
+ When this property is enabled, the style automatically modifies the text property
+ of any symbol style layer whose source is the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox
+ Streets source</a>. On iOS, the user can set the system’s preferred language in
+ Settings, General Settings, Language & Region. On macOS, the user can set the
+ system’s preferred language in the Language & Region pane of System Preferences.
+ */
+@property (nonatomic) BOOL localizesLabels;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 2365641f02..52efc7a85a 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -17,6 +17,7 @@
#import "MGLLight_Private.h"
#import "MGLTileSource_Private.h"
#import "MGLVectorSource.h"
+#import "MGLVectorSource+MGLAdditions.h"
#import "MGLRasterSource.h"
#import "MGLShapeSource.h"
#import "MGLImageSource.h"
@@ -49,12 +50,35 @@
#import "NSImage+MGLAdditions.h"
#endif
+/**
+ Model class for localization changes.
+ */
+@interface MGLTextLanguage: NSObject
+@property (strong, nonatomic) NSString *originalTextField;
+@property (strong, nonatomic) NSString *updatedTextField;
+
+- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField;
+
+@end
+
+@implementation MGLTextLanguage
+- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField
+{
+ if (self = [super init]) {
+ _originalTextField = originalTextField;
+ _updatedTextField = updatedTextField;
+ }
+ return self;
+}
+@end
+
@interface MGLStyle()
@property (nonatomic, readonly, weak) MGLMapView *mapView;
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
@property (readonly, copy, nullable) NSURL *URL;
@property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
+@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier;
@end
@@ -84,8 +108,6 @@ MGL_DEFINE_STYLE(light, light)
MGL_DEFINE_STYLE(dark, dark)
MGL_DEFINE_STYLE(satellite, satellite)
MGL_DEFINE_STYLE(satelliteStreets, satellite-streets)
-MGL_DEFINE_STYLE(trafficDay, traffic-day)
-MGL_DEFINE_STYLE(trafficNight, traffic-night)
// Make sure all the styles listed in mbgl::util::default_styles::orderedStyles
// are defined above and also declared in MGLStyle.h.
@@ -112,6 +134,35 @@ static NSURL *MGLStyleURL_emerald;
return MGLStyleURL_emerald;
}
+// Traffic Day is no longer getting new versions as a default style, so the current version is hard-coded here.
+static NSURL *MGLStyleURL_trafficDay;
++ (NSURL *)trafficDayStyleURL {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ MGLStyleURL_trafficDay = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"];
+ });
+ return MGLStyleURL_trafficDay;
+}
+
++ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version {
+ return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-day-v" stringByAppendingFormat:@"%li", (long)version]];
+}
+
+// Traffic Night is no longer getting new versions as a default style, so the current version is hard-coded here.
+static NSURL *MGLStyleURL_trafficNight;
++ (NSURL *)trafficNightStyleURL {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ MGLStyleURL_trafficNight = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"];
+ });
+ return MGLStyleURL_trafficNight;
+}
+
++ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version {
+ return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-night-v" stringByAppendingFormat:@"%li", (long)version]];
+}
+
+
#pragma mark -
- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView {
@@ -119,6 +170,7 @@ static NSURL *MGLStyleURL_emerald;
_mapView = mapView;
_rawStyle = rawStyle;
_openGLLayers = [NSMutableDictionary dictionary];
+ _localizedLayersByIdentifier = [NSMutableDictionary dictionary];
}
return self;
}
@@ -164,6 +216,7 @@ static NSURL *MGLStyleURL_emerald;
- (MGLSource *)sourceWithIdentifier:(NSString *)identifier
{
auto rawSource = self.rawStyle->getSource(identifier.UTF8String);
+
return rawSource ? [self sourceFromMBGLSource:rawSource] : nil;
}
@@ -175,15 +228,15 @@ static NSURL *MGLStyleURL_emerald;
// TODO: Fill in options specific to the respective source classes
// https://github.com/mapbox/mapbox-gl-native/issues/6584
if (auto vectorSource = rawSource->as<mbgl::style::VectorSource>()) {
- return [[MGLVectorSource alloc] initWithRawSource:vectorSource];
+ return [[MGLVectorSource alloc] initWithRawSource:vectorSource mapView:self.mapView];
} else if (auto geoJSONSource = rawSource->as<mbgl::style::GeoJSONSource>()) {
- return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource];
+ return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource mapView:self.mapView];
} else if (auto rasterSource = rawSource->as<mbgl::style::RasterSource>()) {
- return [[MGLRasterSource alloc] initWithRawSource:rasterSource];
+ return [[MGLRasterSource alloc] initWithRawSource:rasterSource mapView:self.mapView];
} else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) {
- return [[MGLImageSource alloc] initWithRawSource:imageSource];
+ return [[MGLImageSource alloc] initWithRawSource:imageSource mapView:self.mapView];
} else {
- return [[MGLSource alloc] initWithRawSource:rawSource];
+ return [[MGLSource alloc] initWithRawSource:rawSource mapView:self.mapView];
}
}
@@ -197,7 +250,7 @@ static NSURL *MGLStyleURL_emerald;
}
try {
- [source addToStyle:self];
+ [source addToMapView:self.mapView];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantSourceIdentifierException" format:@"%s", err.what()];
}
@@ -211,7 +264,7 @@ static NSURL *MGLStyleURL_emerald;
@"Make sure the source was created as a member of a concrete subclass of MGLSource.",
source];
}
- [source removeFromStyle:self];
+ [source removeFromMapView:self.mapView];
}
- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
@@ -585,4 +638,115 @@ static NSURL *MGLStyleURL_emerald;
self.URL ? [NSString stringWithFormat:@"\"%@\"", self.URL] : self.URL];
}
+#pragma mark Style language preferences
+
+- (void)setLocalizesLabels:(BOOL)localizesLabels
+{
+ if (_localizesLabels != localizesLabels) {
+ _localizesLabels = localizesLabels;
+ } else {
+ return;
+ }
+
+ if (_localizesLabels) {
+ NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage];
+ NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary];
+ for (MGLSymbolStyleLayer *layer in self.layers) {
+ if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
+ continue;
+ }
+
+ MGLVectorSource *source = (MGLVectorSource *)[self sourceWithIdentifier:layer.sourceIdentifier];
+ if (![source isKindOfClass:[MGLVectorSource class]] || !source.mapboxStreets) {
+ continue;
+ }
+
+ NSDictionary *localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier];
+ if (!localizedKeysByKey) {
+ localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier] = [source localizedKeysByKeyForPreferredLanguage:preferredLanguage];
+ }
+
+ NSString *(^stringByLocalizingString)(NSString *) = ^ NSString * (NSString *string) {
+ NSMutableString *localizedString = string.mutableCopy;
+ [localizedKeysByKey enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull localizedKey, BOOL * _Nonnull stop) {
+ NSAssert([key isKindOfClass:[NSString class]], @"key is not a string");
+ NSAssert([localizedKey isKindOfClass:[NSString class]], @"localizedKey is not a string");
+ [localizedString replaceOccurrencesOfString:[NSString stringWithFormat:@"{%@}", key]
+ withString:[NSString stringWithFormat:@"{%@}", localizedKey]
+ options:0
+ range:NSMakeRange(0, localizedString.length)];
+ }];
+ return localizedString;
+ };
+
+ if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
+ NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue];
+ NSString *localizingString = stringByLocalizingString(textField);
+ if (![textField isEqualToString:localizingString]) {
+ MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField
+ updatedTextField:localizingString];
+ [self.localizedLayersByIdentifier setObject:@{ textField : textLanguage } forKey:layer.identifier];
+ layer.text = [MGLStyleValue<NSString *> valueWithRawValue:localizingString];
+ }
+ }
+ else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
+ MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
+ NSMutableDictionary *stops = function.stops.mutableCopy;
+ NSMutableDictionary *cameraStops = [NSMutableDictionary dictionary];
+ [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) {
+ NSString *textField = stop.rawValue;
+ NSString *localizingString = stringByLocalizingString(textField);
+ if (![textField isEqualToString:localizingString]) {
+ MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField
+ updatedTextField:localizingString];
+ [cameraStops setObject:textLanguage forKey:zoomLevel];
+ stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:localizingString];
+ }
+
+ }];
+ if (cameraStops.count > 0) {
+ [self.localizedLayersByIdentifier setObject:cameraStops forKey:layer.identifier];
+ }
+ function.stops = stops;
+ layer.text = function;
+ }
+ }
+ } else {
+
+ [self.localizedLayersByIdentifier enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSDictionary<NSObject *, MGLTextLanguage *> *textFields, BOOL *done) {
+ MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier];
+
+ if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
+ NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue];
+ [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) {
+ if ([textLanguage.updatedTextField isEqualToString:textField]) {
+ layer.text = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField];
+ }
+ }];
+
+ }
+ else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
+ MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
+ NSMutableDictionary *stops = function.stops.mutableCopy;
+ [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *zoomKey, MGLTextLanguage *textLanguage, BOOL *done) {
+ if ([zoomKey isKindOfClass:[NSNumber class]]) {
+ NSNumber *zoomLevel = (NSNumber*)zoomKey;
+ MGLConstantStyleValue<NSString *> *stop = [stops objectForKey:zoomLevel];
+ NSString *textField = stop.rawValue;
+ if ([textLanguage.updatedTextField isEqualToString:textField]) {
+ stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField];
+ }
+ }
+ }];
+
+ function.stops = stops;
+ layer.text = function;
+ }
+
+ }];
+
+ self.localizedLayersByIdentifier = [NSMutableDictionary dictionary];
+ }
+}
+
@end
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index d8dded7dbd..ffb95dfc73 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -8,6 +8,51 @@
NS_ASSUME_NONNULL_BEGIN
/**
+ Part of the icon placed closest to the anchor.
+
+ Values of this type are used in the `MGLSymbolStyleLayer.iconAnchor`
+ property.
+ */
+typedef NS_ENUM(NSUInteger, MGLIconAnchor) {
+ /**
+ The center of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorCenter,
+ /**
+ The left side of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorLeft,
+ /**
+ The right side of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorRight,
+ /**
+ The top of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorTop,
+ /**
+ The bottom of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorBottom,
+ /**
+ The top left corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorTopLeft,
+ /**
+ The top right corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorTopRight,
+ /**
+ The bottom left corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorBottomLeft,
+ /**
+ The bottom right corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorBottomRight,
+};
+
+/**
Orientation of icon when map is pitched.
Values of this type are used in the `MGLSymbolStyleLayer.iconPitchAlignment`
@@ -345,6 +390,34 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconAllowOverlap __attribute__((unavailable("Use iconAllowsOverlap instead.")));
/**
+ Part of the icon placed closest to the anchor.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLIconAnchorCenter`. Set this property to `nil`
+ to reset it to the default value.
+
+ This property is only applied to the style if `iconImageName` is non-`nil`.
+ Otherwise, it is ignored.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLSourceStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ * `MGLInterpolationModeIdentity`
+ * `MGLCompositeStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconAnchor;
+
+/**
If true, other symbols can be visible even if they collide with the icon.
The default value of this property is an `MGLStyleValue` object containing an
@@ -784,6 +857,15 @@ MGL_EXPORT
* `MGLCameraStyleFunction` with an interpolation mode of:
* `MGLInterpolationModeExponential`
* `MGLInterpolationModeInterval`
+ * `MGLSourceStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ * `MGLInterpolationModeIdentity`
+ * `MGLCompositeStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumTextWidth;
@@ -1089,6 +1171,15 @@ MGL_EXPORT
* `MGLCameraStyleFunction` with an interpolation mode of:
* `MGLInterpolationModeExponential`
* `MGLInterpolationModeInterval`
+ * `MGLSourceStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ * `MGLInterpolationModeIdentity`
+ * `MGLCompositeStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
*/
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textLetterSpacing;
@@ -1984,6 +2075,19 @@ MGL_EXPORT
#pragma mark Working with Symbol Style Layer Attribute Values
/**
+ Creates a new value object containing the given `MGLIconAnchor` enumeration.
+
+ @param iconAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLIconAnchor:(MGLIconAnchor)iconAnchor;
+
+/**
+ The `MGLIconAnchor` enumeration representation of the value.
+ */
+@property (readonly) MGLIconAnchor MGLIconAnchorValue;
+
+/**
Creates a new value object containing the given `MGLIconPitchAlignment` enumeration.
@param iconPitchAlignment The value for the new object.
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 2541e6b0a4..1990c82669 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -13,6 +13,18 @@
namespace mbgl {
+ MBGL_DEFINE_ENUM(MGLIconAnchor, {
+ { MGLIconAnchorCenter, "center" },
+ { MGLIconAnchorLeft, "left" },
+ { MGLIconAnchorRight, "right" },
+ { MGLIconAnchorTop, "top" },
+ { MGLIconAnchorBottom, "bottom" },
+ { MGLIconAnchorTopLeft, "top-left" },
+ { MGLIconAnchorTopRight, "top-right" },
+ { MGLIconAnchorBottomLeft, "bottom-left" },
+ { MGLIconAnchorBottomRight, "bottom-right" },
+ });
+
MBGL_DEFINE_ENUM(MGLIconPitchAlignment, {
{ MGLIconPitchAlignmentMap, "map" },
{ MGLIconPitchAlignmentViewport, "viewport" },
@@ -166,6 +178,23 @@ namespace mbgl {
return self.iconAllowsOverlap;
}
+- (void)setIconAnchor:(MGLStyleValue<NSValue *> *)iconAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenPropertyValue(iconAnchor);
+ self.rawLayer->setIconAnchor(mbglValue);
+}
+
+- (MGLStyleValue<NSValue *> *)iconAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getIconAnchor();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconAnchor());
+ }
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(propertyValue);
+}
+
- (void)setIconIgnoresPlacement:(MGLStyleValue<NSNumber *> *)iconIgnoresPlacement {
MGLAssertStyleLayerIsValid();
@@ -456,7 +485,7 @@ namespace mbgl {
- (void)setMaximumTextWidth:(MGLStyleValue<NSNumber *> *)maximumTextWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(maximumTextWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(maximumTextWidth);
self.rawLayer->setTextMaxWidth(mbglValue);
}
@@ -465,9 +494,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextMaxWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextMaxWidth());
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextMaxWidth());
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextMaxWidth:(MGLStyleValue<NSNumber *> *)textMaxWidth {
@@ -586,7 +615,7 @@ namespace mbgl {
- (void)setTextAnchor:(MGLStyleValue<NSValue *> *)textAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toDataDrivenPropertyValue(textAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenPropertyValue(textAnchor);
self.rawLayer->setTextAnchor(mbglValue);
}
@@ -595,9 +624,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextAnchor());
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextAnchor());
}
- return MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextFontNames:(MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
@@ -699,7 +728,7 @@ namespace mbgl {
- (void)setTextLetterSpacing:(MGLStyleValue<NSNumber *> *)textLetterSpacing {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textLetterSpacing);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textLetterSpacing);
self.rawLayer->setTextLetterSpacing(mbglValue);
}
@@ -708,9 +737,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextLetterSpacing();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextLetterSpacing());
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextLetterSpacing());
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextLineHeight:(MGLStyleValue<NSNumber *> *)textLineHeight {
@@ -1344,6 +1373,16 @@ namespace mbgl {
@implementation NSValue (MGLSymbolStyleLayerAdditions)
++ (NSValue *)valueWithMGLIconAnchor:(MGLIconAnchor)iconAnchor {
+ return [NSValue value:&iconAnchor withObjCType:@encode(MGLIconAnchor)];
+}
+
+- (MGLIconAnchor)MGLIconAnchorValue {
+ MGLIconAnchor iconAnchor;
+ [self getValue:&iconAnchor];
+ return iconAnchor;
+}
+
+ (NSValue *)valueWithMGLIconPitchAlignment:(MGLIconPitchAlignment)iconPitchAlignment {
return [NSValue value:&iconPitchAlignment withObjCType:@encode(MGLIconPitchAlignment)];
}
diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h
index b3227e1cdf..5c32791c2f 100644
--- a/platform/darwin/src/MGLTypes.h
+++ b/platform/darwin/src/MGLTypes.h
@@ -47,6 +47,8 @@ typedef NS_ENUM(NSInteger, MGLErrorCode) {
MGLErrorCodeParseStyleFailed = 4,
/** An attempt to load the style failed. */
MGLErrorCodeLoadStyleFailed = 5,
+ /** An error occurred while snapshotting the map. */
+ MGLErrorCodeSnapshotFailed = 6,
};
/** Options for enabling debugging features in an `MGLMapView` instance. */
diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.h b/platform/darwin/src/MGLVectorSource+MGLAdditions.h
index 1e25ee5a60..43b0aba747 100644
--- a/platform/macos/app/MGLVectorSource+MBXAdditions.h
+++ b/platform/darwin/src/MGLVectorSource+MGLAdditions.h
@@ -2,7 +2,7 @@
NS_ASSUME_NONNULL_BEGIN
-@interface MGLVectorSource (MBXAdditions)
+@interface MGLVectorSource (MGLAdditions)
+ (NSString *)preferredMapboxStreetsLanguage;
diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.m b/platform/darwin/src/MGLVectorSource+MGLAdditions.m
index 323bc74366..a305388117 100644
--- a/platform/macos/app/MGLVectorSource+MBXAdditions.m
+++ b/platform/darwin/src/MGLVectorSource+MGLAdditions.m
@@ -1,6 +1,6 @@
-#import "MGLVectorSource+MBXAdditions.h"
+#import "MGLVectorSource+MGLAdditions.h"
-@implementation MGLVectorSource (MBXAdditions)
+@implementation MGLVectorSource (MGLAdditions)
+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages {
// https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
diff --git a/platform/darwin/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm
index 431e0c250c..b1bda56f2d 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorSource.mm
@@ -64,8 +64,8 @@
}
std::vector<mbgl::Feature> features;
- if (self.style) {
- features = self.style.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
+ if (self.mapView) {
+ features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
}
return MGLFeaturesFromMBGLFeatures(features);
}
diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m
index 371ef4023e..cde4bddcc3 100644
--- a/platform/darwin/src/NSString+MGLAdditions.m
+++ b/platform/darwin/src/NSString+MGLAdditions.m
@@ -12,7 +12,16 @@
- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale {
NSMutableString *string = self.mutableCopy;
- [string enumerateLinguisticTagsInRange:string.mgl_wholeRange scheme:NSLinguisticTagSchemeLexicalClass options:0 orthography:nil usingBlock:^(NSString * _Nonnull tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) {
+ NSOrthography *orthography;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) {
+ orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
+ }
+#pragma clang diagnostic pop
+#endif
+ [string enumerateLinguisticTagsInRange:string.mgl_wholeRange scheme:NSLinguisticTagSchemeLexicalClass options:0 orthography:orthography usingBlock:^(NSString * _Nonnull tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) {
NSString *word = [string substringWithRange:tokenRange];
if (word.length > 3
|| !([tag isEqualToString:NSLinguisticTagConjunction]
diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm
index 649cebb47f..4a16ad82fb 100644
--- a/platform/darwin/src/http_file_source.mm
+++ b/platform/darwin/src/http_file_source.mm
@@ -266,7 +266,9 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
NSString *cache_control = [headers objectForKey:@"Cache-Control"];
if (cache_control) {
- response.expires = http::CacheControl::parse([cache_control UTF8String]).toTimePoint();
+ const auto cc = http::CacheControl::parse([cache_control UTF8String]);
+ response.expires = cc.toTimePoint();
+ response.mustRevalidate = cc.mustRevalidate;
}
NSString *expires = [headers objectForKey:@"Expires"];
diff --git a/platform/darwin/src/run_loop.cpp b/platform/darwin/src/run_loop.cpp
index bae8164ab6..2ba8f8415b 100644
--- a/platform/darwin/src/run_loop.cpp
+++ b/platform/darwin/src/run_loop.cpp
@@ -1,38 +1,32 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/async_task.hpp>
-#include <mbgl/util/thread_local.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <CoreFoundation/CoreFoundation.h>
namespace mbgl {
namespace util {
-// Use a static function to avoid the static initialization order fiasco.
-static auto& current() {
- static ThreadLocal<RunLoop> tl;
- return tl;
-};
-
class RunLoop::Impl {
public:
std::unique_ptr<AsyncTask> async;
};
RunLoop* RunLoop::Get() {
- assert(current().get());
- return current().get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
RunLoop::RunLoop(Type)
: impl(std::make_unique<Impl>()) {
- assert(!current().get());
- current().set(this);
+ assert(!Scheduler::GetCurrent());
+ Scheduler::SetCurrent(this);
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
}
RunLoop::~RunLoop() {
- assert(current().get());
- current().set(nullptr);
+ assert(Scheduler::GetCurrent());
+ Scheduler::SetCurrent(nullptr);
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
index ae72b35d82..42c656f203 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -103,6 +103,18 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.source(withIdentifier: "pois"))
}
+
+ func testMGLPolyline() {
+ //#-example-code
+ let coordinates = [
+ CLLocationCoordinate2D(latitude: 35.68476, longitude: -220.24257),
+ CLLocationCoordinate2D(latitude: 37.78428, longitude: -122.41310)
+ ]
+ let polyline = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))
+ //#-end-example-code
+
+ XCTAssertNotNil(polyline)
+ }
func testMGLImageSource() {
//#-example-code
diff --git a/platform/darwin/test/MGLNSStringAdditionsTests.m b/platform/darwin/test/MGLNSStringAdditionsTests.m
index f07f2c1423..03503b7f8a 100644
--- a/platform/darwin/test/MGLNSStringAdditionsTests.m
+++ b/platform/darwin/test/MGLNSStringAdditionsTests.m
@@ -19,7 +19,7 @@
XCTAssertEqualObjects([@"Improve the map" mgl_titleCasedStringWithLocale:locale], @"Improve the Map");
XCTAssertEqualObjects([@"Improve The Map" mgl_titleCasedStringWithLocale:locale], @"Improve The Map");
-
+
XCTAssertEqualObjects([@"Improve a map" mgl_titleCasedStringWithLocale:locale], @"Improve a Map");
XCTAssertEqualObjects([@"Improve A Map" mgl_titleCasedStringWithLocale:locale], @"Improve A Map");
diff --git a/platform/darwin/test/MGLSDKTestHelpers.swift b/platform/darwin/test/MGLSDKTestHelpers.swift
index 82b5caa273..f21041782e 100644
--- a/platform/darwin/test/MGLSDKTestHelpers.swift
+++ b/platform/darwin/test/MGLSDKTestHelpers.swift
@@ -25,7 +25,8 @@ extension MGLSDKTestHelpers {
let methodDescriptionList: UnsafeMutablePointer<objc_method_description>! = protocol_copyMethodDescriptionList(p, false, true, &methodCount)
for i in 0..<Int(methodCount) {
let description: objc_method_description = methodDescriptionList[i]
- methods.insert(description.name.description)
+ XCTAssertNotNil(description.name?.description)
+ methods.insert(description.name!.description)
}
free(methodDescriptionList)
return methods
diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
index 608cfdfd2d..8f610e338c 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -99,6 +99,8 @@
@(mbgl::util::default_styles::satelliteStreets.url));
XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString,
@"mapbox://styles/mapbox/satellite-streets-v99");
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:mbgl::util::default_styles::trafficDay.currentVersion].absoluteString,
@(mbgl::util::default_styles::trafficDay.url));
XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:99].absoluteString,
@@ -107,6 +109,7 @@
@(mbgl::util::default_styles::trafficNight.url));
XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:99].absoluteString,
@"mapbox://styles/mapbox/traffic-night-v99");
+#pragma clang diagnostic pop
static_assert(8 == mbgl::util::default_styles::numOrderedStyles,
"MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles.");
@@ -140,7 +143,7 @@
NSString *styleHeader = self.stringWithContentsOfStyleHeader;
NSError *versionedMethodError;
- NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*;)RE");
+ NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*\b)RE");
NSRegularExpression *versionedMethodExpression = [NSRegularExpression regularExpressionWithPattern:versionedMethodExpressionString options:NSRegularExpressionAnchorsMatchLines error:&versionedMethodError];
XCTAssertNil(versionedMethodError, @"Error compiling regular expression to search for versioned methods.");
NSUInteger numVersionedMethodDeclarations = [versionedMethodExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)];
diff --git a/platform/darwin/test/MGLStyleValueTests.swift b/platform/darwin/test/MGLStyleValueTests.swift
index 784b2fbaf1..c559037588 100644
--- a/platform/darwin/test/MGLStyleValueTests.swift
+++ b/platform/darwin/test/MGLStyleValueTests.swift
@@ -6,16 +6,34 @@ typealias MGLColor = UIColor
#elseif os(macOS)
typealias MGLColor = NSColor
#endif
+
+#if swift(>=3.2)
+#else
+func XCTAssertEqual<T: FloatingPoint>(_ lhs: @autoclosure () throws -> T, _ rhs: @autoclosure () throws -> T, accuracy: T) {
+ XCTAssertEqualWithAccuracy(lhs, rhs, accuracy: accuracy)
+}
+#endif
extension MGLStyleValueTests {
+
+ struct Color {
+ var red: CGFloat = 0
+ var green: CGFloat = 0
+ var blue: CGFloat = 0
+ var alpha: CGFloat = 0
+ }
+
func assertColorsEqualWithAccuracy(_ actual: MGLColor, _ expected: MGLColor, accuracy: Float = 1/255) {
- var actualComponents : [CGFloat] = [0, 0, 0, 0]
- var expectedComponents : [CGFloat] = [0, 0, 0, 0]
- actual.getRed(&(actualComponents[0]), green: &(actualComponents[1]), blue: &(actualComponents[2]), alpha: &(actualComponents[3]))
- expected.getRed(&(expectedComponents[0]), green: &(expectedComponents[1]), blue: &(expectedComponents[2]), alpha: &(expectedComponents[3]))
- for (ac, ec) in zip(actualComponents, expectedComponents) {
- XCTAssertEqualWithAccuracy(Float(ac), Float(ec), accuracy: accuracy)
- }
+ var actualColor = Color()
+ var expectedColor = Color()
+
+ actual.getRed(&actualColor.red, green: &actualColor.green, blue: &actualColor.blue, alpha: &actualColor.alpha)
+ expected.getRed(&expectedColor.red, green: &expectedColor.green, blue: &expectedColor.blue, alpha: &expectedColor.alpha)
+
+ XCTAssertEqual(Float(actualColor.red), Float(expectedColor.red), accuracy: accuracy)
+ XCTAssertEqual(Float(actualColor.green), Float(expectedColor.green), accuracy: accuracy)
+ XCTAssertEqual(Float(actualColor.blue), Float(expectedColor.blue), accuracy: accuracy)
+ XCTAssertEqual(Float(actualColor.alpha), Float(expectedColor.alpha), accuracy: accuracy)
}
func assertColorValuesEqual(_ actual: MGLStyleValue<MGLColor>, _ expected: MGLStyleValue<MGLColor>) {
@@ -249,22 +267,50 @@ extension MGLStyleValueTests {
options: [.defaultValue: defaultRadius]
)
circleStyleLayer.circleRadius = expectedCompositeCategoricalValue
- XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeCategoricalValue)
+
+ var compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction
+ var expectedCompositeValue = expectedCompositeCategoricalValue as! MGLCompositeStyleFunction
+ XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName)
+ XCTAssertEqual(compositeValue.stops as NSDictionary, radiusCompositeCategoricalStops as NSDictionary)
+ XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode)
+ XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue)
// data-driven, composite function with inner exponential color stop values nested in outer camera stops
let radiusCompositeExponentialOrIntervalStops: [Float: [Float: MGLStyleValue<NSNumber>]] = [
- 0: [0: smallRadius],
- 10: [200: smallRadius],
- 20: [200: largeRadius]
+ 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
+ ]
+
+ let expectedStops = [
+ 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
]
+ circleStyleLayer.circleRadius = MGLStyleValue<NSNumber>(
+ interpolationMode: .exponential,
+ compositeStops: [
+ 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
+ ],
+ attributeName: "temp",
+ options: [.defaultValue: mediumRadius]
+ )
+
let expectedCompositeExponentialValue = MGLStyleValue<NSNumber>(
interpolationMode: .exponential,
compositeStops: radiusCompositeExponentialOrIntervalStops,
attributeName: "temp",
options: [.defaultValue: mediumRadius]
)
- circleStyleLayer.circleRadius = expectedCompositeExponentialValue
- XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeExponentialValue)
+
+ compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction
+ expectedCompositeValue = expectedCompositeExponentialValue as! MGLCompositeStyleFunction
+ XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName)
+ XCTAssertEqual(compositeValue.stops as NSDictionary, expectedStops as NSDictionary)
+ XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode)
+ XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue)
// get a value back
if let returnedCircleRadius = circleStyleLayer.circleRadius as? MGLCompositeStyleFunction<NSNumber> {
@@ -287,11 +333,30 @@ extension MGLStyleValueTests {
// data-driven, composite function with inner interval color stop values nested in outer camera stops
let expectedCompositeIntervalValue = MGLStyleValue<NSNumber>(
interpolationMode: .interval,
- compositeStops: radiusCompositeExponentialOrIntervalStops,
+ compositeStops: [
+
+ 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
+ ],
+ attributeName: "temp",
+ options: nil
+ )
+ circleStyleLayer.circleRadius = MGLStyleValue<NSNumber>(
+ interpolationMode: .interval,
+ compositeStops: [
+ 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)],
+ 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)]
+ ],
attributeName: "temp",
options: nil
)
- circleStyleLayer.circleRadius = expectedCompositeIntervalValue
- XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeIntervalValue)
+
+ compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction
+ expectedCompositeValue = expectedCompositeIntervalValue as! MGLCompositeStyleFunction
+ XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName)
+ XCTAssertEqual(compositeValue.stops as NSDictionary, expectedStops as NSDictionary)
+ XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode)
+ XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue)
}
}
diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
index 6b0b20354b..1ac86dd402 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -87,6 +87,40 @@
XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
+ // icon-anchor
+ {
+ XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(),
+ @"icon-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconAnchor;
+
+ MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomRight]];
+ layer.iconAnchor = constantStyleValue;
+ mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType> propertyValue = { mbgl::style::SymbolAnchorType::BottomRight };
+ XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue,
+ @"Setting iconAnchor to a constant value should update icon-anchor.");
+ XCTAssertEqualObjects(layer.iconAnchor, constantStyleValue,
+ @"iconAnchor should round-trip constant values.");
+
+ MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.iconAnchor = functionStyleValue;
+
+ mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = { {{18, mbgl::style::SymbolAnchorType::BottomRight}} };
+ propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue,
+ @"Setting iconAnchor to a camera function should update icon-anchor.");
+ XCTAssertEqualObjects(layer.iconAnchor, functionStyleValue,
+ @"iconAnchor should round-trip camera functions.");
+
+
+
+ layer.iconAnchor = nil;
+ XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(),
+ @"Unsetting iconAnchor should return icon-anchor to the default value.");
+ XCTAssertEqualObjects(layer.iconAnchor, defaultStyleValue,
+ @"iconAnchor should return the default value after being unset.");
+ }
+
// icon-ignore-placement
{
XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(),
@@ -702,7 +736,7 @@
MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
layer.maximumTextWidth = constantStyleValue;
- mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
@"Setting maximumTextWidth to a constant value should update text-max-width.");
XCTAssertEqualObjects(layer.maximumTextWidth, constantStyleValue,
@@ -719,6 +753,29 @@
XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
@"maximumTextWidth should round-trip camera functions.");
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.maximumTextWidth = functionStyleValue;
+
+ mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
+ @"Setting maximumTextWidth to a source function should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
+ @"maximumTextWidth should round-trip source functions.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.maximumTextWidth = functionStyleValue;
+
+ std::map<float, float> innerStops { {18, 0xff} };
+ mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
+
+ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
+
+ XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
+ @"Setting maximumTextWidth to a composite function should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
+ @"maximumTextWidth should round-trip composite functions.");
layer.maximumTextWidth = nil;
@@ -726,11 +783,6 @@
@"Unsetting maximumTextWidth should return text-max-width to the default value.");
XCTAssertEqualObjects(layer.maximumTextWidth, defaultStyleValue,
@"maximumTextWidth should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.maximumTextWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.maximumTextWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// symbol-avoid-edges
@@ -931,7 +983,7 @@
MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomRight]];
layer.textAnchor = constantStyleValue;
- mbgl::style::DataDrivenPropertyValue<mbgl::style::TextAnchorType> propertyValue = { mbgl::style::TextAnchorType::BottomRight };
+ mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType> propertyValue = { mbgl::style::SymbolAnchorType::BottomRight };
XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue,
@"Setting textAnchor to a constant value should update text-anchor.");
XCTAssertEqualObjects(layer.textAnchor, constantStyleValue,
@@ -940,8 +992,8 @@
MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
layer.textAnchor = functionStyleValue;
- mbgl::style::IntervalStops<mbgl::style::TextAnchorType> intervalStops = { {{18, mbgl::style::TextAnchorType::BottomRight}} };
- propertyValue = mbgl::style::CameraFunction<mbgl::style::TextAnchorType> { intervalStops };
+ mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = { {{18, mbgl::style::SymbolAnchorType::BottomRight}} };
+ propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops };
XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue,
@"Setting textAnchor to a camera function should update text-anchor.");
@@ -1134,7 +1186,7 @@
MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
layer.textLetterSpacing = constantStyleValue;
- mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
@"Setting textLetterSpacing to a constant value should update text-letter-spacing.");
XCTAssertEqualObjects(layer.textLetterSpacing, constantStyleValue,
@@ -1151,6 +1203,29 @@
XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
@"textLetterSpacing should round-trip camera functions.");
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.textLetterSpacing = functionStyleValue;
+
+ mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
+ @"Setting textLetterSpacing to a source function should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
+ @"textLetterSpacing should round-trip source functions.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.textLetterSpacing = functionStyleValue;
+
+ std::map<float, float> innerStops { {18, 0xff} };
+ mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
+
+ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
+
+ XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
+ @"Setting textLetterSpacing to a composite function should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
+ @"textLetterSpacing should round-trip composite functions.");
layer.textLetterSpacing = nil;
@@ -1158,11 +1233,6 @@
@"Unsetting textLetterSpacing should return text-letter-spacing to the default value.");
XCTAssertEqualObjects(layer.textLetterSpacing, defaultStyleValue,
@"textLetterSpacing should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textLetterSpacing = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textLetterSpacing = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// text-line-height
@@ -2345,6 +2415,7 @@
- (void)testPropertyNames {
[self testPropertyName:@"icon-allows-overlap" isBoolean:YES];
+ [self testPropertyName:@"icon-anchor" isBoolean:NO];
[self testPropertyName:@"icon-ignores-placement" isBoolean:YES];
[self testPropertyName:@"icon-image-name" isBoolean:NO];
[self testPropertyName:@"icon-offset" isBoolean:NO];
@@ -2396,6 +2467,15 @@
}
- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorCenter].MGLIconAnchorValue, MGLIconAnchorCenter);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorLeft].MGLIconAnchorValue, MGLIconAnchorLeft);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorRight].MGLIconAnchorValue, MGLIconAnchorRight);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorTop].MGLIconAnchorValue, MGLIconAnchorTop);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorBottom].MGLIconAnchorValue, MGLIconAnchorBottom);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorTopLeft].MGLIconAnchorValue, MGLIconAnchorTopLeft);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorTopRight].MGLIconAnchorValue, MGLIconAnchorTopRight);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomLeft].MGLIconAnchorValue, MGLIconAnchorBottomLeft);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomRight].MGLIconAnchorValue, MGLIconAnchorBottomRight);
XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentMap].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentMap);
XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentViewport].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentViewport);
XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentAuto);
diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp
index bf8d7b6348..3fdb03e6b4 100644
--- a/platform/default/default_file_source.cpp
+++ b/platform/default/default_file_source.cpp
@@ -28,10 +28,15 @@ namespace mbgl {
class DefaultFileSource::Impl {
public:
- Impl(ActorRef<Impl>, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize)
+ Impl(ActorRef<Impl> self, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize)
: assetFileSource(assetFileSource_)
- , localFileSource(std::make_unique<LocalFileSource>())
- , offlineDatabase(cachePath, maximumCacheSize) {
+ , localFileSource(std::make_unique<LocalFileSource>()) {
+ // Initialize the Database asynchronously so as to not block Actor creation.
+ self.invoke(&Impl::initializeOfflineDatabase, cachePath, maximumCacheSize);
+ }
+
+ void initializeOfflineDatabase(std::string cachePath, uint64_t maximumCacheSize) {
+ offlineDatabase = std::make_unique<OfflineDatabase>(cachePath, maximumCacheSize);
}
void setAPIBaseURL(const std::string& url) {
@@ -56,7 +61,7 @@ public:
void listRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) {
try {
- callback({}, offlineDatabase.listRegions());
+ callback({}, offlineDatabase->listRegions());
} catch (...) {
callback(std::current_exception(), {});
}
@@ -66,7 +71,7 @@ public:
const OfflineRegionMetadata& metadata,
std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) {
try {
- callback({}, offlineDatabase.createRegion(definition, metadata));
+ callback({}, offlineDatabase->createRegion(definition, metadata));
} catch (...) {
callback(std::current_exception(), {});
}
@@ -76,7 +81,7 @@ public:
const OfflineRegionMetadata& metadata,
std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) {
try {
- callback({}, offlineDatabase.updateMetadata(regionID, metadata));
+ callback({}, offlineDatabase->updateMetadata(regionID, metadata));
} catch (...) {
callback(std::current_exception(), {});
}
@@ -93,7 +98,7 @@ public:
void deleteRegion(OfflineRegion&& region, std::function<void (std::exception_ptr)> callback) {
try {
downloads.erase(region.getID());
- offlineDatabase.deleteRegion(std::move(region));
+ offlineDatabase->deleteRegion(std::move(region));
callback({});
} catch (...) {
callback(std::current_exception());
@@ -121,11 +126,10 @@ public:
tasks[req] = localFileSource->request(resource, callback);
} else {
// Try the offline database
- Resource revalidation = resource;
-
- const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires;
+ const bool hasPrior = resource.priorEtag || resource.priorModified ||
+ resource.priorExpires || resource.priorData;
if (!hasPrior || resource.necessity == Resource::Optional) {
- auto offlineResponse = offlineDatabase.get(resource);
+ auto offlineResponse = offlineDatabase->get(resource);
if (resource.necessity == Resource::Optional && !offlineResponse) {
// Ensure there's always a response that we can send, so the caller knows that
@@ -137,17 +141,34 @@ public:
}
if (offlineResponse) {
- revalidation.priorModified = offlineResponse->modified;
- revalidation.priorExpires = offlineResponse->expires;
- revalidation.priorEtag = offlineResponse->etag;
- callback(*offlineResponse);
+ resource.priorModified = offlineResponse->modified;
+ resource.priorExpires = offlineResponse->expires;
+ resource.priorEtag = offlineResponse->etag;
+
+ // Don't return resources the server requested not to show when they're stale.
+ // Even if we can't directly use the response, we may still use it to send a
+ // conditional HTTP request.
+ if (offlineResponse->isUsable()) {
+ callback(*offlineResponse);
+ } else if (resource.necessity == Resource::Optional) {
+ // Instead of the data that we got, return a not found error so that
+ // underlying implementations know about the fact that we couldn't find
+ // usable cache data.
+ offlineResponse->error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, "Cached resource is unusable");
+ callback(*offlineResponse);
+ } else {
+ // Since we can't return the data immediately, we'll have to hold on so that
+ // we can return it later in case we get a 304 Not Modified response.
+ resource.priorData = offlineResponse->data;
+ }
}
}
// Get from the online file source
if (resource.necessity == Resource::Required) {
- tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) mutable {
- this->offlineDatabase.put(revalidation, onlineResponse);
+ tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) mutable {
+ this->offlineDatabase->put(resource, onlineResponse);
callback(onlineResponse);
});
}
@@ -159,11 +180,15 @@ public:
}
void setOfflineMapboxTileCountLimit(uint64_t limit) {
- offlineDatabase.setOfflineMapboxTileCountLimit(limit);
+ offlineDatabase->setOfflineMapboxTileCountLimit(limit);
+ }
+
+ void setOnlineStatus(const bool status) {
+ onlineFileSource.setOnlineStatus(status);
}
void put(const Resource& resource, const Response& response) {
- offlineDatabase.put(resource, response);
+ offlineDatabase->put(resource, response);
}
private:
@@ -173,13 +198,13 @@ private:
return *it->second;
}
return *downloads.emplace(regionID,
- std::make_unique<OfflineDownload>(regionID, offlineDatabase.getRegionDefinition(regionID), offlineDatabase, onlineFileSource)).first->second;
+ std::make_unique<OfflineDownload>(regionID, offlineDatabase->getRegionDefinition(regionID), *offlineDatabase, onlineFileSource)).first->second;
}
// shared so that destruction is done on the creating thread
const std::shared_ptr<FileSource> assetFileSource;
const std::unique_ptr<FileSource> localFileSource;
- OfflineDatabase offlineDatabase;
+ std::unique_ptr<OfflineDatabase> offlineDatabase;
OnlineFileSource onlineFileSource;
std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
@@ -288,6 +313,10 @@ void DefaultFileSource::resume() {
// For testing only:
+void DefaultFileSource::setOnlineStatus(const bool status) {
+ impl->actor().invoke(&Impl::setOnlineStatus, status);
+}
+
void DefaultFileSource::put(const Resource& resource, const Response& response) {
impl->actor().invoke(&Impl::put, resource, response);
}
diff --git a/src/mbgl/storage/file_source_request.cpp b/platform/default/file_source_request.cpp
index 8a6fb21181..09ea8cc32a 100644
--- a/src/mbgl/storage/file_source_request.cpp
+++ b/platform/default/file_source_request.cpp
@@ -1,13 +1,13 @@
#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/actor/mailbox.hpp>
-#include <mbgl/util/run_loop.hpp>
+#include <mbgl/actor/scheduler.hpp>
namespace mbgl {
FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback)
: responseCallback(callback)
- , mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())) {
+ , mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())) {
}
FileSourceRequest::~FileSourceRequest() {
diff --git a/platform/default/http_file_source.cpp b/platform/default/http_file_source.cpp
index 867d85fa4d..a9c442c2de 100644
--- a/platform/default/http_file_source.cpp
+++ b/platform/default/http_file_source.cpp
@@ -325,7 +325,9 @@ size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const
baton->response->etag = std::string(buffer + begin, length - begin - 2); // remove \r\n
} else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->expires = http::CacheControl::parse(value.c_str()).toTimePoint();
+ const auto cc = http::CacheControl::parse(value.c_str());
+ baton->response->expires = cc.toTimePoint();
+ baton->response->mustRevalidate = cc.mustRevalidate;
} else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
baton->response->expires = Timestamp{ Seconds(curl_getdate(value.c_str(), nullptr)) };
diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp
index a854fe9731..edf637a560 100644
--- a/platform/default/mbgl/gl/headless_backend.cpp
+++ b/platform/default/mbgl/gl/headless_backend.cpp
@@ -11,9 +11,9 @@ namespace mbgl {
class HeadlessBackend::View {
public:
- View(gl::Context& context, Size size)
- : color(context.createRenderbuffer<gl::RenderbufferType::RGBA>(size)),
- depthStencil(context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size)),
+ View(gl::Context& context, Size size_)
+ : color(context.createRenderbuffer<gl::RenderbufferType::RGBA>(size_)),
+ depthStencil(context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size_)),
framebuffer(context.createFramebuffer(color, depthStencil)) {
}
@@ -64,6 +64,10 @@ void HeadlessBackend::bind() {
context_.viewport = { 0, 0, size };
}
+Size HeadlessBackend::getFramebufferSize() const {
+ return size;
+}
+
void HeadlessBackend::updateAssumedState() {
// no-op
}
diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp
index 8d86ea8c47..66f861e213 100644
--- a/platform/default/mbgl/gl/headless_backend.hpp
+++ b/platform/default/mbgl/gl/headless_backend.hpp
@@ -15,6 +15,7 @@ public:
~HeadlessBackend() override;
void bind() override;
+ Size getFramebufferSize() const override;
void updateAssumedState() override;
void setSize(Size);
diff --git a/platform/default/mbgl/gl/headless_frontend.cpp b/platform/default/mbgl/gl/headless_frontend.cpp
index ad03706be7..5d2932258a 100644
--- a/platform/default/mbgl/gl/headless_frontend.cpp
+++ b/platform/default/mbgl/gl/headless_frontend.cpp
@@ -5,11 +5,11 @@
namespace mbgl {
-HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler)
- : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler) {
+HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir)
+ : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir) {
}
-HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler)
+HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir)
: size(size_),
pixelRatio(pixelRatio_),
backend({ static_cast<uint32_t>(size.width * pixelRatio),
@@ -20,7 +20,7 @@ HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fi
renderer->render(*updateParameters);
}
}),
- renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler)) {
+ renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler, GLContextMode::Unique, programCacheDir)) {
}
HeadlessFrontend::~HeadlessFrontend() = default;
diff --git a/platform/default/mbgl/gl/headless_frontend.hpp b/platform/default/mbgl/gl/headless_frontend.hpp
index 18d0d2527b..33503bc13b 100644
--- a/platform/default/mbgl/gl/headless_frontend.hpp
+++ b/platform/default/mbgl/gl/headless_frontend.hpp
@@ -3,6 +3,7 @@
#include <mbgl/renderer/renderer_frontend.hpp>
#include <mbgl/gl/headless_backend.hpp>
#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/optional.hpp>
#include <memory>
@@ -16,8 +17,8 @@ class Map;
class HeadlessFrontend : public RendererFrontend {
public:
- HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&);
- HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&);
+ HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {});
+ HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional<std::string> programCacheDir = {});
~HeadlessFrontend() override;
void reset() override;
diff --git a/platform/default/mbgl/map/map_snapshotter.cpp b/platform/default/mbgl/map/map_snapshotter.cpp
new file mode 100644
index 0000000000..95c46344fe
--- /dev/null
+++ b/platform/default/mbgl/map/map_snapshotter.cpp
@@ -0,0 +1,76 @@
+#include <mbgl/map/map_snapshotter.hpp>
+
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/event.hpp>
+
+namespace mbgl {
+
+class MapSnapshotter::Impl {
+public:
+ Impl(FileSource&,
+ Scheduler&,
+ const std::string& styleURL,
+ const Size&,
+ const float pixelRatio,
+ const CameraOptions&,
+ const optional<LatLngBounds> region,
+ const optional<std::string> programCacheDir);
+
+ void snapshot(ActorRef<MapSnapshotter::Callback>);
+
+private:
+ HeadlessFrontend frontend;
+ Map map;
+};
+
+MapSnapshotter::Impl::Impl(FileSource& fileSource,
+ Scheduler& scheduler,
+ const std::string& styleURL,
+ const Size& size,
+ const float pixelRatio,
+ const CameraOptions& cameraOptions,
+ const optional<LatLngBounds> region,
+ const optional<std::string> programCacheDir)
+ : frontend(size, pixelRatio, fileSource, scheduler, programCacheDir)
+ , map(frontend, MapObserver::nullObserver(), size, pixelRatio, fileSource, scheduler, MapMode::Still) {
+
+ map.getStyle().loadURL(styleURL);
+
+ map.jumpTo(cameraOptions);
+
+ // Set region, if specified
+ if (region) {
+ mbgl::EdgeInsets insets = { 0, 0, 0, 0 };
+ std::vector<LatLng> latLngs = { region->southwest(), region->northeast() };
+ map.jumpTo(map.cameraForLatLngs(latLngs, insets));
+ }
+}
+
+void MapSnapshotter::Impl::snapshot(ActorRef<MapSnapshotter::Callback> callback) {
+ map.renderStill([this, callback = std::move(callback)] (std::exception_ptr error) mutable {
+ callback.invoke(&MapSnapshotter::Callback::operator(), error, error ? PremultipliedImage() : frontend.readStillImage());
+ });
+}
+
+MapSnapshotter::MapSnapshotter(FileSource& fileSource,
+ Scheduler& scheduler,
+ const std::string& styleURL,
+ const Size& size,
+ const float pixelRatio,
+ const CameraOptions& cameraOptions,
+ const optional<LatLngBounds> region,
+ const optional<std::string> programCacheDir)
+ : impl(std::make_unique<util::Thread<MapSnapshotter::Impl>>("Map Snapshotter", fileSource, scheduler, styleURL, size, pixelRatio, cameraOptions, region, programCacheDir)) {
+}
+
+MapSnapshotter::~MapSnapshotter() = default;
+
+void MapSnapshotter::snapshot(ActorRef<MapSnapshotter::Callback> callback) {
+ impl->actor().invoke(&Impl::snapshot, std::move(callback));
+}
+
+} // namespace mbgl
diff --git a/platform/default/mbgl/map/map_snapshotter.hpp b/platform/default/mbgl/map/map_snapshotter.hpp
new file mode 100644
index 0000000000..0afa848fd8
--- /dev/null
+++ b/platform/default/mbgl/map/map_snapshotter.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <exception>
+#include <string>
+#include <functional>
+
+namespace mbgl {
+
+template<class> class ActorRef;
+struct CameraOptions;
+class FileSource;
+class Size;
+class LatLngBounds;
+
+class MapSnapshotter {
+public:
+ MapSnapshotter(FileSource& fileSource,
+ Scheduler& scheduler,
+ const std::string& styleURL,
+ const Size&,
+ const float pixelRatio,
+ const CameraOptions&,
+ const optional<LatLngBounds> region,
+ const optional<std::string> cacheDir = {});
+
+ ~MapSnapshotter();
+
+ using Callback = std::function<void (std::exception_ptr, PremultipliedImage)>;
+ void snapshot(ActorRef<Callback>);
+
+private:
+ class Impl;
+ std::unique_ptr<util::Thread<Impl>> impl;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/storage/file_source_request.hpp b/platform/default/mbgl/storage/file_source_request.hpp
index 6bd0d44df6..6bd0d44df6 100644
--- a/src/mbgl/storage/file_source_request.hpp
+++ b/platform/default/mbgl/storage/file_source_request.hpp
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp
index fd2d47819b..9ec789f725 100644
--- a/platform/default/mbgl/storage/offline.cpp
+++ b/platform/default/mbgl/storage/offline.cpp
@@ -1,6 +1,7 @@
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/projection.hpp>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
@@ -24,17 +25,11 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
}
std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
- double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min);
- double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max);
-
- assert(minZ >= 0);
- assert(maxZ >= 0);
- assert(minZ < std::numeric_limits<uint8_t>::max());
- assert(maxZ < std::numeric_limits<uint8_t>::max());
+ const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
std::vector<CanonicalTileID> result;
- for (uint8_t z = minZ; z <= maxZ; z++) {
+ for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
for (const auto& tile : util::tileCover(bounds, z)) {
result.emplace_back(tile.canonical);
}
@@ -43,6 +38,28 @@ std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(Sourc
return result;
}
+uint64_t OfflineTilePyramidRegionDefinition::tileCount(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
+
+ const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange);
+ unsigned long result = 0;;
+ for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
+ result += util::tileCount(bounds, z, tileSize);
+ }
+
+ return result;
+}
+
+Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const {
+ double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min);
+ double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max);
+
+ assert(minZ >= 0);
+ assert(maxZ >= 0);
+ assert(minZ < std::numeric_limits<uint8_t>::max());
+ assert(maxZ < std::numeric_limits<uint8_t>::max());
+ return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) };
+}
+
OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) {
rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
doc.Parse<0>(region.c_str());
diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp
index bad4ccdbf1..65c2097182 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -49,8 +49,9 @@ void OfflineDatabase::ensureSchema() {
case 2: migrateToVersion3(); // fall through
case 3: // no-op and fall through
case 4: migrateToVersion5(); // fall through
- case 5: return;
- default: throw std::runtime_error("unknown schema version");
+ case 5: migrateToVersion6(); // fall through
+ case 6: return;
+ default: break; // downgrade, delete the database
}
removeExisting();
@@ -83,7 +84,7 @@ void OfflineDatabase::ensureSchema() {
db->exec("PRAGMA journal_mode = DELETE");
db->exec("PRAGMA synchronous = FULL");
db->exec(schema);
- db->exec("PRAGMA user_version = 5");
+ db->exec("PRAGMA user_version = 6");
} catch (...) {
Log::Error(Event::Database, "Unexpected error creating database schema: %s", util::toString(std::current_exception()).c_str());
throw;
@@ -126,6 +127,14 @@ void OfflineDatabase::migrateToVersion5() {
db->exec("PRAGMA user_version = 5");
}
+void OfflineDatabase::migrateToVersion6() {
+ mapbox::sqlite::Transaction transaction(*db);
+ db->exec("ALTER TABLE resources ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0");
+ db->exec("ALTER TABLE tiles ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0");
+ db->exec("PRAGMA user_version = 6");
+ transaction.commit();
+}
+
OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) {
auto it = statements.find(sql);
@@ -211,8 +220,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
// clang-format off
Statement stmt = getStatement(
- // 0 1 2 3 4
- "SELECT etag, expires, modified, data, compressed "
+ // 0 1 2 3 4 5
+ "SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM resources "
"WHERE url = ?");
// clang-format on
@@ -226,14 +235,15 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.modified = stmt->get<optional<Timestamp>>(2);
+ response.etag = stmt->get<optional<std::string>>(0);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.mustRevalidate = stmt->get<bool>(2);
+ response.modified = stmt->get<optional<Timestamp>>(3);
- optional<std::string> data = stmt->get<optional<std::string>>(3);
+ optional<std::string> data = stmt->get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<int>(4)) {
+ } else if (stmt->get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -265,14 +275,16 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement update = getStatement(
"UPDATE resources "
- "SET accessed = ?1, "
- " expires = ?2 "
- "WHERE url = ?3 ");
+ "SET accessed = ?1, "
+ " expires = ?2, "
+ " must_revalidate = ?3 "
+ "WHERE url = ?4 ");
// clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
- update->bind(3, resource.url);
+ update->bind(3, response.mustRevalidate);
+ update->bind(4, resource.url);
update->run();
return false;
}
@@ -286,29 +298,31 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement update = getStatement(
"UPDATE resources "
- "SET kind = ?1, "
- " etag = ?2, "
- " expires = ?3, "
- " modified = ?4, "
- " accessed = ?5, "
- " data = ?6, "
- " compressed = ?7 "
- "WHERE url = ?8 ");
+ "SET kind = ?1, "
+ " etag = ?2, "
+ " expires = ?3, "
+ " must_revalidate = ?4, "
+ " modified = ?5, "
+ " accessed = ?6, "
+ " data = ?7, "
+ " compressed = ?8 "
+ "WHERE url = ?9 ");
// clang-format on
update->bind(1, int(resource.kind));
update->bind(2, response.etag);
update->bind(3, response.expires);
- update->bind(4, response.modified);
- update->bind(5, util::now());
- update->bind(8, resource.url);
+ update->bind(4, response.mustRevalidate);
+ update->bind(5, response.modified);
+ update->bind(6, util::now());
+ update->bind(9, resource.url);
if (response.noContent) {
- update->bind(6, nullptr);
- update->bind(7, false);
+ update->bind(7, nullptr);
+ update->bind(8, false);
} else {
- update->bindBlob(6, data.data(), data.size(), false);
- update->bind(7, compressed);
+ update->bindBlob(7, data.data(), data.size(), false);
+ update->bind(8, compressed);
}
update->run();
@@ -319,23 +333,24 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement insert = getStatement(
- "INSERT INTO resources (url, kind, etag, expires, modified, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) ");
+ "INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ");
// clang-format on
insert->bind(1, resource.url);
insert->bind(2, int(resource.kind));
insert->bind(3, response.etag);
insert->bind(4, response.expires);
- insert->bind(5, response.modified);
- insert->bind(6, util::now());
+ insert->bind(5, response.mustRevalidate);
+ insert->bind(6, response.modified);
+ insert->bind(7, util::now());
if (response.noContent) {
- insert->bind(7, nullptr);
- insert->bind(8, false);
+ insert->bind(8, nullptr);
+ insert->bind(9, false);
} else {
- insert->bindBlob(7, data.data(), data.size(), false);
- insert->bind(8, compressed);
+ insert->bindBlob(8, data.data(), data.size(), false);
+ insert->bind(9, compressed);
}
insert->run();
@@ -366,8 +381,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
// clang-format off
Statement stmt = getStatement(
- // 0 1 2 3 4
- "SELECT etag, expires, modified, data, compressed "
+ // 0 1 2, 3, 4, 5
+ "SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM tiles "
"WHERE url_template = ?1 "
" AND pixel_ratio = ?2 "
@@ -389,14 +404,15 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.modified = stmt->get<optional<Timestamp>>(2);
+ response.etag = stmt->get<optional<std::string>>(0);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.mustRevalidate = stmt->get<bool>(2);
+ response.modified = stmt->get<optional<Timestamp>>(3);
- optional<std::string> data = stmt->get<optional<std::string>>(3);
+ optional<std::string> data = stmt->get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<int>(4)) {
+ } else if (stmt->get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -440,22 +456,24 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement update = getStatement(
"UPDATE tiles "
- "SET accessed = ?1, "
- " expires = ?2 "
- "WHERE url_template = ?3 "
- " AND pixel_ratio = ?4 "
- " AND x = ?5 "
- " AND y = ?6 "
- " AND z = ?7 ");
+ "SET accessed = ?1, "
+ " expires = ?2, "
+ " must_revalidate = ?3 "
+ "WHERE url_template = ?4 "
+ " AND pixel_ratio = ?5 "
+ " AND x = ?6 "
+ " AND y = ?7 "
+ " AND z = ?8 ");
// clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
- update->bind(3, tile.urlTemplate);
- update->bind(4, tile.pixelRatio);
- update->bind(5, tile.x);
- update->bind(6, tile.y);
- update->bind(7, tile.z);
+ update->bind(3, response.mustRevalidate);
+ update->bind(4, tile.urlTemplate);
+ update->bind(5, tile.pixelRatio);
+ update->bind(6, tile.x);
+ update->bind(7, tile.y);
+ update->bind(8, tile.z);
update->run();
return false;
}
@@ -469,35 +487,37 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement update = getStatement(
"UPDATE tiles "
- "SET modified = ?1, "
- " etag = ?2, "
- " expires = ?3, "
- " accessed = ?4, "
- " data = ?5, "
- " compressed = ?6 "
- "WHERE url_template = ?7 "
- " AND pixel_ratio = ?8 "
- " AND x = ?9 "
- " AND y = ?10 "
- " AND z = ?11 ");
+ "SET modified = ?1, "
+ " etag = ?2, "
+ " expires = ?3, "
+ " must_revalidate = ?4, "
+ " accessed = ?5, "
+ " data = ?6, "
+ " compressed = ?7 "
+ "WHERE url_template = ?8 "
+ " AND pixel_ratio = ?9 "
+ " AND x = ?10 "
+ " AND y = ?11 "
+ " AND z = ?12 ");
// clang-format on
update->bind(1, response.modified);
update->bind(2, response.etag);
update->bind(3, response.expires);
- update->bind(4, util::now());
- update->bind(7, tile.urlTemplate);
- update->bind(8, tile.pixelRatio);
- update->bind(9, tile.x);
- update->bind(10, tile.y);
- update->bind(11, tile.z);
+ update->bind(4, response.mustRevalidate);
+ update->bind(5, util::now());
+ update->bind(8, tile.urlTemplate);
+ update->bind(9, tile.pixelRatio);
+ update->bind(10, tile.x);
+ update->bind(11, tile.y);
+ update->bind(12, tile.z);
if (response.noContent) {
- update->bind(5, nullptr);
- update->bind(6, false);
+ update->bind(6, nullptr);
+ update->bind(7, false);
} else {
- update->bindBlob(5, data.data(), data.size(), false);
- update->bind(6, compressed);
+ update->bindBlob(6, data.data(), data.size(), false);
+ update->bind(7, compressed);
}
update->run();
@@ -508,8 +528,8 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement insert = getStatement(
- "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, etag, expires, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11) ");
+ "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, must_revalidate, etag, expires, accessed, data, compressed) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)");
// clang-format on
insert->bind(1, tile.urlTemplate);
@@ -518,16 +538,17 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
insert->bind(4, tile.y);
insert->bind(5, tile.z);
insert->bind(6, response.modified);
- insert->bind(7, response.etag);
- insert->bind(8, response.expires);
- insert->bind(9, util::now());
+ insert->bind(7, response.mustRevalidate);
+ insert->bind(8, response.etag);
+ insert->bind(9, response.expires);
+ insert->bind(10, util::now());
if (response.noContent) {
- insert->bind(10, nullptr);
- insert->bind(11, false);
+ insert->bind(11, nullptr);
+ insert->bind(12, false);
} else {
- insert->bindBlob(10, data.data(), data.size(), false);
- insert->bind(11, compressed);
+ insert->bindBlob(11, data.data(), data.size(), false);
+ insert->bind(12, compressed);
}
insert->run();
diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp
index 57ffcee4eb..91b544a9e0 100644
--- a/platform/default/mbgl/storage/offline_database.hpp
+++ b/platform/default/mbgl/storage/offline_database.hpp
@@ -64,6 +64,7 @@ private:
void removeExisting();
void migrateToVersion3();
void migrateToVersion5();
+ void migrateToVersion6();
class Statement {
public:
diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp
index 7f0001f64b..ff61114888 100644
--- a/platform/default/mbgl/storage/offline_download.cpp
+++ b/platform/default/mbgl/storage/offline_download.cpp
@@ -80,7 +80,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) {
if (urlOrTileset.is<Tileset>()) {
result.requiredResourceCount +=
- definition.tileCover(type, tileSize, urlOrTileset.get<Tileset>().zoomRange).size();
+ definition.tileCount(type, tileSize, urlOrTileset.get<Tileset>().zoomRange);
} else {
result.requiredResourceCount += 1;
const auto& url = urlOrTileset.get<std::string>();
@@ -90,7 +90,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error);
if (tileset) {
result.requiredResourceCount +=
- definition.tileCover(type, tileSize, (*tileset).zoomRange).size();
+ definition.tileCount(type, tileSize, (*tileset).zoomRange);
}
} else {
result.requiredResourceCountIsPrecise = false;
diff --git a/platform/default/mbgl/storage/offline_schema.cpp.include b/platform/default/mbgl/storage/offline_schema.cpp.include
index a80c7677e6..41af81e55b 100644
--- a/platform/default/mbgl/storage/offline_schema.cpp.include
+++ b/platform/default/mbgl/storage/offline_schema.cpp.include
@@ -10,6 +10,7 @@ static const char * schema =
" data BLOB,\n"
" compressed INTEGER NOT NULL DEFAULT 0,\n"
" accessed INTEGER NOT NULL,\n"
+" must_revalidate INTEGER NOT NULL DEFAULT 0,\n"
" UNIQUE (url)\n"
");\n"
"CREATE TABLE tiles (\n"
@@ -25,6 +26,7 @@ static const char * schema =
" data BLOB,\n"
" compressed INTEGER NOT NULL DEFAULT 0,\n"
" accessed INTEGER NOT NULL,\n"
+" must_revalidate INTEGER NOT NULL DEFAULT 0,\n"
" UNIQUE (url_template, pixel_ratio, z, x, y)\n"
");\n"
"CREATE TABLE regions (\n"
diff --git a/platform/default/mbgl/storage/offline_schema.sql b/platform/default/mbgl/storage/offline_schema.sql
index 9df8fa6a89..722b0e0451 100644
--- a/platform/default/mbgl/storage/offline_schema.sql
+++ b/platform/default/mbgl/storage/offline_schema.sql
@@ -8,6 +8,7 @@ CREATE TABLE resources ( -- Generic table for style, source, s
data BLOB,
compressed INTEGER NOT NULL DEFAULT 0,
accessed INTEGER NOT NULL,
+ must_revalidate INTEGER NOT NULL DEFAULT 0,
UNIQUE (url)
);
@@ -24,6 +25,7 @@ CREATE TABLE tiles (
data BLOB,
compressed INTEGER NOT NULL DEFAULT 0,
accessed INTEGER NOT NULL,
+ must_revalidate INTEGER NOT NULL DEFAULT 0,
UNIQUE (url_template, pixel_ratio, z, x, y)
);
diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp
index f10e0f8ffb..d685109b95 100644
--- a/platform/default/online_file_source.cpp
+++ b/platform/default/online_file_source.cpp
@@ -117,13 +117,24 @@ public:
}
void activateRequest(OnlineFileRequest* request) {
- activeRequests.insert(request);
- request->request = httpFileSource.request(request->resource, [=] (Response response) {
+ auto callback = [=](Response response) {
activeRequests.erase(request);
- activatePendingRequest();
request->request.reset();
request->completed(response);
- });
+ activatePendingRequest();
+ };
+
+ activeRequests.insert(request);
+
+ if (online) {
+ request->request = httpFileSource.request(request->resource, callback);
+ } else {
+ Response response;
+ response.error = std::make_unique<Response::Error>(Response::Error::Reason::Connection,
+ "Online connectivity is disabled.");
+ callback(response);
+ }
+
assert(pendingRequestsMap.size() == pendingRequestsList.size());
}
@@ -153,6 +164,11 @@ public:
resourceTransform = std::move(transform);
}
+ void setOnlineStatus(const bool status) {
+ online = status;
+ networkIsReachableAgain();
+ }
+
private:
void networkIsReachableAgain() {
for (auto& request : allRequests) {
@@ -178,6 +194,7 @@ private:
std::unordered_map<OnlineFileRequest*, std::list<OnlineFileRequest*>::iterator> pendingRequestsMap;
std::unordered_set<OnlineFileRequest*> activeRequests;
+ bool online = true;
HTTPFileSource httpFileSource;
util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) };
};
@@ -320,6 +337,14 @@ void OnlineFileRequest::completed(Response response) {
resource.priorModified = response.modified;
}
+ if (response.notModified && resource.priorData) {
+ // When the priorData field is set, it indicates that we had to revalidate the request and
+ // that the requestor hasn't gotten data yet. If we get a 304 response, this means that we
+ // have send the cached data to give the requestor a chance to actually obtain the data.
+ response.data = std::move(resource.priorData);
+ response.notModified = false;
+ }
+
bool isExpired = false;
if (response.expires) {
@@ -375,10 +400,16 @@ ActorRef<OnlineFileRequest> OnlineFileRequest::actor() {
if (!mailbox) {
// Lazy constructed because this can be costly and
// the ResourceTransform is not used by many apps.
- mailbox = std::make_shared<Mailbox>(*util::RunLoop::Get());
+ mailbox = std::make_shared<Mailbox>(*Scheduler::GetCurrent());
}
return ActorRef<OnlineFileRequest>(*this, mailbox);
}
+// For testing only:
+
+void OnlineFileSource::setOnlineStatus(const bool status) {
+ impl->setOnlineStatus(status);
+}
+
} // namespace mbgl
diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp
index 98d1badcb5..6375dba78e 100644
--- a/platform/default/run_loop.cpp
+++ b/platform/default/run_loop.cpp
@@ -1,6 +1,7 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/thread_local.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <uv.h>
@@ -10,9 +11,6 @@
namespace {
-using namespace mbgl::util;
-static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
-
void dummyCallback(uv_async_t*) {}
} // namespace
@@ -53,7 +51,8 @@ struct Watch {
};
RunLoop* RunLoop::Get() {
- return current.get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
class RunLoop::Impl {
@@ -98,12 +97,12 @@ RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
impl->type = type;
- current.set(this);
+ Scheduler::SetCurrent(this);
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
}
RunLoop::~RunLoop() {
- current.set(nullptr);
+ Scheduler::SetCurrent(nullptr);
// Close the dummy handle that we have
// just to keep the main loop alive.
@@ -127,7 +126,7 @@ RunLoop::~RunLoop() {
}
LOOP_HANDLE RunLoop::getLoopHandle() {
- return current.get()->impl->loop;
+ return Get()->impl->loop;
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp
index 269c97716f..2e08354fdf 100644
--- a/platform/default/sqlite3.cpp
+++ b/platform/default/sqlite3.cpp
@@ -287,6 +287,11 @@ bool Statement::run() {
}
}
+template <> bool Statement::get(int offset) {
+ assert(impl);
+ return sqlite3_column_int(impl->stmt, offset);
+}
+
template <> int Statement::get(int offset) {
assert(impl);
return sqlite3_column_int(impl->stmt, offset);
diff --git a/platform/default/thread_local.cpp b/platform/default/thread_local.cpp
index 7abbaa0146..db70773c12 100644
--- a/platform/default/thread_local.cpp
+++ b/platform/default/thread_local.cpp
@@ -58,8 +58,8 @@ void ThreadLocal<T>::set(T* ptr) {
}
}
-template class ThreadLocal<RunLoop>;
template class ThreadLocal<BackendScope>;
+template class ThreadLocal<Scheduler>;
template class ThreadLocal<int>; // For unit tests
} // namespace util
diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp
index 784e908878..d591f897e0 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -110,6 +110,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
printf("- Press `N` to reset north\n");
printf("- Press `R` to enable the route demo\n");
printf("- Press `E` to insert an example building extrusion layer\n");
+ printf("- Press `O` to toggle online connectivity\n");
printf("- Press `Z` to cycle through north orientations\n");
printf("- Prezz `X` to cycle through the viewport modes\n");
printf("- Press `A` to cycle through Mapbox offices in the world + dateline monument\n");
@@ -176,6 +177,9 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
if (!mods)
view->map->resetPosition();
break;
+ case GLFW_KEY_O:
+ view->onlineStatusCallback();
+ break;
case GLFW_KEY_S:
if (view->changeStyleCallback)
view->changeStyleCallback();
diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp
index ccde4f027f..d3257f6b9a 100644
--- a/platform/glfw/glfw_view.hpp
+++ b/platform/glfw/glfw_view.hpp
@@ -28,6 +28,10 @@ public:
pauseResumeCallback = callback;
};
+ void setOnlineStatusCallback(std::function<void()> callback) {
+ onlineStatusCallback = callback;
+ }
+
void setShouldClose();
void setWindowTitle(const std::string&);
@@ -37,7 +41,7 @@ public:
void invalidate();
mbgl::Size getSize() const;
- mbgl::Size getFramebufferSize() const;
+ mbgl::Size getFramebufferSize() const override;
// mbgl::RendererBackend implementation
void bind() override;
@@ -111,6 +115,7 @@ private:
std::function<void()> changeStyleCallback;
std::function<void()> pauseResumeCallback;
+ std::function<void()> onlineStatusCallback;
std::function<void(mbgl::Map*)> animateRouteCallback;
mbgl::util::RunLoop runLoop;
diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp
index 6418ddcf78..dadd88c705 100644
--- a/platform/glfw/main.cpp
+++ b/platform/glfw/main.cpp
@@ -34,16 +34,17 @@ void quit_handler(int) {
}
int main(int argc, char *argv[]) {
+ // Load settings
+ mbgl::Settings_JSON settings;
+
bool fullscreen = false;
bool benchmark = false;
std::string style;
- double latitude = 0, longitude = 0;
- double bearing = 0, zoom = 1, pitch = 0;
- bool skipConfig = false;
const struct option long_options[] = {
{"fullscreen", no_argument, nullptr, 'f'},
{"benchmark", no_argument, nullptr, 'b'},
+ {"offline", no_argument, nullptr, 'o'},
{"style", required_argument, nullptr, 's'},
{"lon", required_argument, nullptr, 'x'},
{"lat", required_argument, nullptr, 'y'},
@@ -59,37 +60,32 @@ int main(int argc, char *argv[]) {
if (opt == -1) break;
switch (opt)
{
- case 0:
- if (long_options[option_index].flag != nullptr)
- break;
case 'f':
fullscreen = true;
break;
case 'b':
benchmark = true;
break;
+ case 'o':
+ settings.online = false;
+ break;
case 's':
- style = std::string("asset://") + std::string(optarg);
+ style = std::string(optarg);
break;
case 'x':
- longitude = atof(optarg);
- skipConfig = true;
+ settings.longitude = atof(optarg);
break;
case 'y':
- latitude = atof(optarg);
- skipConfig = true;
+ settings.latitude = atof(optarg);
break;
case 'z':
- zoom = atof(optarg);
- skipConfig = true;
+ settings.zoom = atof(optarg);
break;
case 'r':
- bearing = atof(optarg);
- skipConfig = true;
+ settings.bearing = atof(optarg);
break;
case 'p':
- pitch = atof(optarg);
- skipConfig = true;
+ settings.pitch = atof(optarg);
break;
default:
break;
@@ -112,6 +108,10 @@ int main(int argc, char *argv[]) {
view = &backend;
mbgl::DefaultFileSource fileSource("/tmp/mbgl-cache.db", ".");
+ if (!settings.online) {
+ fileSource.setOnlineStatus(false);
+ mbgl::Log::Warning(mbgl::Event::Setup, "Application is offline. Press `O` to toggle online status.");
+ }
// Set access token if present
const char *token = getenv("MAPBOX_ACCESS_TOKEN");
@@ -127,21 +127,21 @@ int main(int argc, char *argv[]) {
backend.setMap(&map);
- // Load settings
- mbgl::Settings_JSON settings;
-
- if (skipConfig) {
- map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
- map.setBearing(bearing);
- map.setPitch(pitch);
- mbgl::Log::Info(mbgl::Event::General, "Location: %f/%f (z%.2f, %.2f deg)", latitude, longitude, zoom, bearing);
- } else {
- map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom);
- map.setBearing(settings.bearing);
- map.setPitch(settings.pitch);
- map.setDebug(mbgl::MapDebugOptions(settings.debug));
+ if (!style.empty() && style.find("://") == std::string::npos) {
+ style = std::string("file://") + style;
}
+ map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom);
+ map.setBearing(settings.bearing);
+ map.setPitch(settings.pitch);
+ map.setDebug(mbgl::MapDebugOptions(settings.debug));
+
+ view->setOnlineStatusCallback([&settings, &fileSource]() {
+ settings.online = !settings.online;
+ fileSource.setOnlineStatus(settings.online);
+ mbgl::Log::Info(mbgl::Event::Setup, "Application is %s. Press `O` to toggle online status.", settings.online ? "online" : "offline");
+ });
+
view->setChangeStyleCallback([&map] () {
static uint8_t currentStyleIndex;
@@ -193,9 +193,7 @@ int main(int argc, char *argv[]) {
settings.bearing = map.getBearing();
settings.pitch = map.getPitch();
settings.debug = mbgl::EnumType(map.getDebug());
- if (!skipConfig) {
- settings.save();
- }
+ settings.save();
mbgl::Log::Info(mbgl::Event::General,
R"(Exit location: --lat="%f" --lon="%f" --zoom="%f" --bearing "%f")",
settings.latitude, settings.longitude, settings.zoom, settings.bearing);
diff --git a/platform/glfw/settings_json.cpp b/platform/glfw/settings_json.cpp
index 2ba1038dc7..5b6aa4e0da 100644
--- a/platform/glfw/settings_json.cpp
+++ b/platform/glfw/settings_json.cpp
@@ -14,6 +14,7 @@ void Settings_JSON::load() {
file >> bearing;
file >> pitch;
file >> debug;
+ file >> online;
}
}
@@ -26,6 +27,7 @@ void Settings_JSON::save() {
file << bearing << std::endl;
file << pitch << std::endl;
file << debug << std::endl;
+ file << online << std::endl;
}
}
@@ -36,6 +38,7 @@ void Settings_JSON::clear() {
bearing = 0;
pitch = 0;
debug = 0;
+ online = true;
}
} // namespace mbgl
diff --git a/platform/glfw/settings_json.hpp b/platform/glfw/settings_json.hpp
index eb23b28bc8..49ea00e3e1 100644
--- a/platform/glfw/settings_json.hpp
+++ b/platform/glfw/settings_json.hpp
@@ -19,6 +19,7 @@ public:
double pitch = 0;
EnumType debug = 0;
+ bool online = true;
};
} // namespace mbgl
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 785b0ee78b..37a44549ba 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -4,13 +4,69 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
## master
+### Networking and storage
+
+* Added a new `MGLMapSnapshotter` class for capturing rendered map images from an `MGLMapView`’s camera. ([#9891](https://github.com/mapbox/mapbox-gl-native/pull/9891))
+* Reduced the time it takes to create new `MGLMapView` instances in some cases. ([#9864](https://github.com/mapbox/mapbox-gl-native/pull/9864))
+* Added support for forced cache revalidation that will eliminate flickering that was sometimes visible for certain types of tiles (e.g., traffic tiles). ([#9670](https://github.com/mapbox/mapbox-gl-native/pull/9670), [#9103](https://github.com/mapbox/mapbox-gl-native/issues/9103))
+* Improved the performance of the SDK when parsing vector tile data used to render the map. ([#9312](https://github.com/mapbox/mapbox-gl-native/pull/9312))
+
### Styles
-* Added support for displaying geo-referenced images via the `MGLImageSource`. [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
-* The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+* Added a new type of source, represented by the `MGLImageSource` class at runtime, that displays a georeferenced image. ([#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110))
+* Setting a style using `MGLMapView`'s `styleURL` property now smoothly transitions from the previous style to the new style and maintains equivalent layers and sources along with their identifiers. ([#9256](https://github.com/mapbox/mapbox-gl-native/pull/9256))
+* Added `MGLCircleStyleLayer.circlePitchAlignment` and `MGLSymbolStyleLayer.iconPitchAlignment` properties to control whether circles and symbols lie flat against a tilted map. ([#9426](https://github.com/mapbox/mapbox-gl-native/pull/9426), [#9479](https://github.com/mapbox/mapbox-gl-native/pull/9479))
+* Added an `MGLSymbolStyleLayer.iconAnchor` property to control where an icon is anchored. ([#9849](https://github.com/mapbox/mapbox-gl-native/pull/9849))
+* The `maximumTextWidth` and `textLetterSpacing` properties of `MGLSymbolStyleLayer` are now compatible with `MGLSourceStyleFunction`s and `MGLCompositeStyleFunction`s, allowing data-driven styling of these properties. ([#9870](https://github.com/mapbox/mapbox-gl-native/pull/9870))
+* Improved the legibility of labels that follow lines when the map is tilted. ([#9009](https://github.com/mapbox/mapbox-gl-native/pull/9009))
+* Fixed an issue that could cause flickering when a translucent raster style layer was present. ([#9468](https://github.com/mapbox/mapbox-gl-native/pull/9468))
+* Fixed an issue that could cause antialiasing between polygons on the same layer to fail if the fill layers used data-driven styling for the fill color. ([#9699](https://github.com/mapbox/mapbox-gl-native/pull/9699))
+* The previously deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+
+### Annotations and user interaction
+
+* Fixed several bugs and performance issues related to the use of annotations backed by `MGLAnnotationImage`. The limits on the number and size of images and glyphs has been effectively eliminated and should now depend on hardware constraints. These fixes also apply to images used to represent icons in `MGLSymbolStyleLayer`. ([#9213](https://github.com/mapbox/mapbox-gl-native/pull/9213))
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
+* Selecting an annotation no longer sets the user tracking mode to `MGLUserTrackingModeNone`. ([#10094](https://github.com/mapbox/mapbox-gl-native/pull/10094))
+* Added `-[MGLMapView cameraThatFitsShape:direction:edgePadding:]` to get a camera with zoom level and center coordinate computed to fit a shape. ([#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107))
+
+### Other changes
+
+* Fixed an issue that could cause line label rendering glitches when the line geometry is projected to a point behind the plane of the camera. ([#9865](https://github.com/mapbox/mapbox-gl-native/pull/9865))
+* Fixed an issue that could cause a crash when using `-[MGLMapView flyToCamera:completionHandler:]` and related methods with zoom levels at or near the maximum value. ([#9381](https://github.com/mapbox/mapbox-gl-native/pull/9381))
+* Added `-[MGLMapView showAttribution:]` to allow custom attribution buttons to show the default attribution interface. ([#10085](https://github.com/mapbox/mapbox-gl-native/pull/10085))
+
+## 3.6.4 - September 25, 2017
+
+* Fixed an issue where stale (but still valid) map data could be ignored in offline mode. ([#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012))
+
+## 3.6.3 - September 15, 2017
+
+* Added the option to display an always-on heading indicator with the default user location annotation, controlled via the `MGLMapView.showsUserHeadingIndicator` property. ([#9886](https://github.com/mapbox/mapbox-gl-native/pull/9886))
+* Fixed an issue where user heading tracking mode would update too frequently. ([#9845](https://github.com/mapbox/mapbox-gl-native/pull/9845))
+* Added support for iOS 11 location usage descriptions. ([#9869](https://github.com/mapbox/mapbox-gl-native/pull/9869))
+* Fixed an issue where `MGLUserLocation.location` did not follow its documented initialization behavior. This property will now properly return `nil` until the user’s location has been determined. ([#9639](https://github.com/mapbox/mapbox-gl-native/pull/9639))
+* `MGLMapView`’s `minimumZoomLevel` and `maximumZoomLevel` properties are now available in Interface Builder’s Attributes inspector. ([#9729](https://github.com/mapbox/mapbox-gl-native/pull/9729))
+* Deprecated `+[MGLStyle trafficDayStyleURL]` and `+[MGLStyle trafficNightStyleURL]` with no replacement method. To use the Traffic Day and Traffic Night styles going forward, we recommend that you use the underlying URL. ([#9918](https://github.com/mapbox/mapbox-gl-native/pull/9918))
+* Fixed a crash that sometimes occurred when a map view's view controller was deallocated. ([#9995](https://github.com/mapbox/mapbox-gl-native/pull/9995))
+
+## 3.6.2 - August 18, 2017
+
+* Added an `MGLStyle.localizesLabels` property, off by default, that localizes any Mapbox Streets–sourced symbol layer into the user’s preferred language. ([#9582](https://github.com/mapbox/mapbox-gl-native/pull/9582))
+* Added an additional camera method to MGLMapView that accepts an edge padding parameter. ([#9651](https://github.com/mapbox/mapbox-gl-native/pull/9651))
+* Fixed an issue with the scaling of the user location annotation’s horizontal accuracy indicator. ([#9721](https://github.com/mapbox/mapbox-gl-native/pull/9721))
+* Fixed an issue that caused `-[MGLShapeSource featuresMatchingPredicate:]` and `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` to always return an empty array. ([#9784](https://github.com/mapbox/mapbox-gl-native/pull/9784))
+
+## 3.6.1 - July 28, 2017
+
+* Reduced the size of the dynamic framework by optimizing symbol visibility. ([#7604](https://github.com/mapbox/mapbox-gl-native/pull/7604))
+* Fixed an issue where the attribution button would have its custom tint color reset when the map view received a tint color change notification, such as when an alert controller was presented. ([#9598](https://github.com/mapbox/mapbox-gl-native/pull/9598))
+* Improved the behavior of zoom gestures when the map reaches the minimum zoom limit. ([#9626](https://github.com/mapbox/mapbox-gl-native/pull/9626))
+* Fixed an issue where tilt gesture was triggered with two fingers aligned vertically and panning down. ([#9571](https://github.com/mapbox/mapbox-gl-native/pull/9571))
+* Bitcode symbol maps (.bcsymbolmap files) are now included with the dynamic framework. ([#9613](https://github.com/mapbox/mapbox-gl-native/pull/9613))
-## 3.6.0
+## 3.6.0 - June 29, 2017
### Packaging
diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md
index bcb837c243..43d7ef3c75 100644
--- a/platform/ios/DEVELOPING.md
+++ b/platform/ios/DEVELOPING.md
@@ -151,13 +151,6 @@ make darwin-update-examples
`make ios-test` builds and runs unit tests of cross-platform code as well as the SDK.
-Before you can run unit tests of the cross-platform code on the command line, install ios-sim version 3.2.0 (not any other version):
-
-```bash
-brew tap mapbox/homebrew-ios-sim-3
-brew install mapbox/homebrew-ios-sim-3/ios-sim
-```
-
To instead run the cross-platform tests in Xcode instead of on the command line:
1. Run `make iproj` to set up the workspace.
diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
index c7b14ead3b..1abcaf467c 100644
--- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '3.6.0-alpha.1'
+ version = '3.7.0-alpha.1'
m.name = 'Mapbox-iOS-SDK-nightly-dynamic'
m.version = "#{version}-nightly"
diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
index d2a686f1fb..ceb7f21c47 100644
--- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '3.6.0'
+ version = '3.7.0-alpha.1'
m.name = 'Mapbox-iOS-SDK-symbols'
m.version = "#{version}-symbols"
diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec
index 55e8791b4c..bcd37d0d06 100644
--- a/platform/ios/Mapbox-iOS-SDK.podspec
+++ b/platform/ios/Mapbox-iOS-SDK.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '3.6.0'
+ version = '3.7.0-alpha.1'
m.name = 'Mapbox-iOS-SDK'
m.version = version
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
index e1bc22272f..f4e2027f0f 100644
--- a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -97,6 +97,11 @@
"idiom" : "ipad",
"filename" : "Icon-83.5@2x.png",
"scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
}
],
"info" : {
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json b/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
index 1eeddba9b9..228b81a818 100644
--- a/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
+++ b/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
@@ -2,24 +2,11 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "settings.png",
- "scale" : "1x"
- },
- {
- "idiom" : "universal",
- "filename" : "settings@2x.png",
- "scale" : "2x"
- },
- {
- "idiom" : "universal",
- "scale" : "3x"
+ "filename" : "settings.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
- },
- "properties" : {
- "template-rendering-intent" : "template"
}
} \ No newline at end of file
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/settings.pdf b/platform/ios/app/Assets.xcassets/settings.imageset/settings.pdf
new file mode 100644
index 0000000000..46aa7443f0
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/settings.imageset/settings.pdf
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/settings.png b/platform/ios/app/Assets.xcassets/settings.imageset/settings.png
deleted file mode 100644
index 5d7643eef5..0000000000
--- a/platform/ios/app/Assets.xcassets/settings.imageset/settings.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png b/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png
deleted file mode 100644
index 2bb9f0ebad..0000000000
--- a/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/Default-568h@2x.png b/platform/ios/app/Default-568h@2x.png
deleted file mode 100644
index 0891b7aabf..0000000000
--- a/platform/ios/app/Default-568h@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/Info.plist b/platform/ios/app/Info.plist
index d5b6825422..a98a8c10c5 100644
--- a/platform/ios/app/Info.plist
+++ b/platform/ios/app/Info.plist
@@ -27,9 +27,11 @@
<key>NSHumanReadableCopyright</key>
<string>© 2014–2017 Mapbox</string>
<key>NSLocationAlwaysUsageDescription</key>
- <string>The map will ALWAYS display the user’s location.</string>
+ <string>The map will display your location. The map may also use your location when it isn’t visible in order to improve OpenStreetMap and Mapbox products.</string>
<key>NSLocationWhenInUseUsageDescription</key>
- <string>The map will display the user’s location.</string>
+ <string>The map will display your location.</string>
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
+ <string>The map will display your location. If you choose Always, the map may also use your location when it isn’t visible in order to improve OpenStreetMap and Mapbox products.</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
@@ -51,5 +53,16 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
+ <key>UIApplicationShortcutItems</key>
+ <array>
+ <dict>
+ <key>UIApplicationShortcutItemTitle</key>
+ <string>Settings</string>
+ <key>UIApplicationShortcutItemType</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER).settings</string>
+ <key>UIApplicationShortcutItemIconFile</key>
+ <string>settings</string>
+ </dict>
+ </array>
</dict>
</plist>
diff --git a/platform/ios/app/LaunchScreen.storyboard b/platform/ios/app/LaunchScreen.storyboard
index 323bd43177..299e186886 100644
--- a/platform/ios/app/LaunchScreen.storyboard
+++ b/platform/ios/app/LaunchScreen.storyboard
@@ -1,8 +1,12 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <device id="retina4_7" orientation="portrait">
+ <adaptation id="fullscreen"/>
+ </device>
<dependencies>
<deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
@@ -14,9 +18,9 @@
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
diff --git a/platform/ios/app/MBXAppDelegate.m b/platform/ios/app/MBXAppDelegate.m
index c2834bfa7f..1934f4912b 100644
--- a/platform/ios/app/MBXAppDelegate.m
+++ b/platform/ios/app/MBXAppDelegate.m
@@ -26,4 +26,22 @@ NSString * const MBXMapboxAccessTokenDefaultsKey = @"MBXMapboxAccessToken";
return YES;
}
+#pragma mark - Quick actions
+
+- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
+ completionHandler([self handleShortcut:shortcutItem]);
+}
+
+- (BOOL)handleShortcut:(UIApplicationShortcutItem *)shortcut {
+ if ([[shortcut.type componentsSeparatedByString:@"."].lastObject isEqual:@"settings"]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
+ });
+
+ return YES;
+ }
+
+ return NO;
+}
+
@end
diff --git a/platform/ios/app/MBXSnapshotsViewController.h b/platform/ios/app/MBXSnapshotsViewController.h
new file mode 100644
index 0000000000..f791602e98
--- /dev/null
+++ b/platform/ios/app/MBXSnapshotsViewController.h
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+
+@interface MBXSnapshotsViewController : UIViewController
+
+@end
diff --git a/platform/ios/app/MBXSnapshotsViewController.m b/platform/ios/app/MBXSnapshotsViewController.m
new file mode 100644
index 0000000000..ab5ad97c90
--- /dev/null
+++ b/platform/ios/app/MBXSnapshotsViewController.m
@@ -0,0 +1,66 @@
+#import "MBXSnapshotsViewController.h"
+
+#import <Mapbox/Mapbox.h>
+
+@interface MBXSnapshotsViewController ()
+
+// Top row
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewTL;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewTM;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewTR;
+
+// Bottom row
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewBL;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewBM;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewBR;
+
+@end
+
+@implementation MBXSnapshotsViewController {
+ // Top row
+ MGLMapSnapshotter* topLeftSnapshotter;
+ MGLMapSnapshotter* topCenterSnapshotter;
+ MGLMapSnapshotter* topRightSnapshotter;
+
+ // Bottom row
+ MGLMapSnapshotter* bottomLeftSnapshotter;
+ MGLMapSnapshotter* bottomCenterSnapshotter;
+ MGLMapSnapshotter* bottomRightSnapshotter;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Start snapshotters
+ topLeftSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewTL coordinates:CLLocationCoordinate2DMake(37.7184, -122.4365)];
+ topCenterSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewTM coordinates:CLLocationCoordinate2DMake(38.8936, -77.0146)];
+ topRightSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewTR coordinates:CLLocationCoordinate2DMake(-13.1356, -74.2442)];
+
+ bottomLeftSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewBL coordinates:CLLocationCoordinate2DMake(52.5072, 13.4247)];
+ bottomCenterSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewBM coordinates:CLLocationCoordinate2DMake(60.2118, 24.6754)];
+ bottomRightSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewBR coordinates:CLLocationCoordinate2DMake(31.2780, 121.4286)];
+}
+
+- (MGLMapSnapshotter*) startSnapshotterForImageView:(UIImageView*) imageView coordinates:(CLLocationCoordinate2D) coordinates {
+ // Create snapshot options
+ MGLMapCamera* mapCamera = [[MGLMapCamera alloc] init];
+ mapCamera.pitch = 20;
+ mapCamera.centerCoordinate = coordinates;
+ MGLMapSnapshotOptions* options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:[MGLStyle satelliteStreetsStyleURL] camera:mapCamera size:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];
+ options.zoomLevel = 10;
+
+ // Create and start the snapshotter
+ MGLMapSnapshotter* snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options];
+ [snapshotter startWithCompletionHandler: ^(UIImage *image, NSError *error) {
+ if (error) {
+ NSLog(@"Could not load snapshot: %@", [error localizedDescription]);
+ } else {
+ imageView.image = image;
+ }
+ }];
+
+ return snapshotter;
+}
+
+
+@end
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 29c5c65012..07838bc6bd 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -34,7 +34,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsCoreRenderingRows) {
MBXSettingsCoreRenderingTimestamps,
MBXSettingsCoreRenderingCollisionBoxes,
MBXSettingsCoreRenderingOverdrawVisualization,
- MBXSettingsCoreRenderingToggleTwoMaps,
};
typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
@@ -48,6 +47,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
MBXSettingsAnnotationsTestShapes,
MBXSettingsAnnotationsCustomCallout,
MBXSettingsAnnotationsQueryAnnotations,
+ MBXSettingsAnnotationsCustomUserDot,
MBXSettingsAnnotationsRemoveAnnotations,
};
@@ -73,7 +73,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
MBXSettingsRuntimeStylingVectorSource,
MBXSettingsRuntimeStylingRasterSource,
MBXSettingsRuntimeStylingImageSource,
- MBXSettingsRuntimeStylingCountryLabels,
MBXSettingsRuntimeStylingRouteLine,
MBXSettingsRuntimeStylingDDSPolygon,
};
@@ -81,9 +80,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MBXSettingsMiscellaneousShowReuseQueueStats = 0,
MBXSettingsMiscellaneousWorldTour,
- MBXSettingsMiscellaneousCustomUserDot,
MBXSettingsMiscellaneousShowZoomLevel,
MBXSettingsMiscellaneousScrollView,
+ MBXSettingsMiscellaneousToggleTwoMaps,
+ MBXSettingsMiscellaneousCountryLabels,
+ MBXSettingsMiscellaneousShowSnapshots,
MBXSettingsMiscellaneousPrintLogFile,
MBXSettingsMiscellaneousDeleteLogFile,
};
@@ -114,7 +115,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@property (nonatomic) IBOutlet MGLMapView *mapView;
-@property (weak, nonatomic) IBOutlet UILabel *hudLabel;
+@property (weak, nonatomic) IBOutlet UIButton *hudLabel;
@property (nonatomic) NSInteger styleIndex;
@property (nonatomic) BOOL debugLoggingEnabled;
@property (nonatomic) BOOL customUserLocationAnnnotationEnabled;
@@ -162,7 +163,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
self.mapView.scaleBar.hidden = NO;
+ self.mapView.showsUserHeadingIndicator = YES;
self.hudLabel.hidden = YES;
+ self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular];
if ([MGLAccountManager accessToken].length)
{
@@ -305,8 +308,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
(debugMask & MGLMapDebugCollisionBoxesMask ? @"Hide" :@"Show")],
[NSString stringWithFormat:@"%@ Overdraw Visualization",
(debugMask & MGLMapDebugOverdrawVisualizationMask ? @"Hide" :@"Show")],
- [NSString stringWithFormat:@"%@ Second Map",
- ([self.view viewWithTag:2] == nil ? @"Show" : @"Hide")],
]];
break;
case MBXSettingsAnnotations:
@@ -321,6 +322,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Add Test Shapes",
@"Add Point With Custom Callout",
@"Query Annotations",
+ [NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")],
@"Remove Annotations",
]];
break;
@@ -347,7 +349,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Style Vector Source",
@"Style Raster Source",
@"Style Image Source",
- [NSString stringWithFormat:@"Label Countries in %@", (_usingLocaleBasedCountryLabels ? @"Local Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])],
@"Add Route Line",
@"Dynamically Style Polygon",
]];
@@ -356,9 +357,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[settingsTitles addObjectsFromArray:@[
[NSString stringWithFormat:@"%@ Reuse Queue Stats", (_reuseQueueStatsEnabled ? @"Hide" :@"Show")],
@"Start World Tour",
- [NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")],
- [NSString stringWithFormat:@"%@ Zoom Level", (_showZoomLevelEnabled ? @"Hide" :@"Show")],
+ [NSString stringWithFormat:@"%@ Zoom/Pitch/Direction Label", (_showZoomLevelEnabled ? @"Hide" :@"Show")],
@"Embedded Map View",
+ [NSString stringWithFormat:@"%@ Second Map", ([self.view viewWithTag:2] == nil ? @"Show" : @"Hide")],
+ [NSString stringWithFormat:@"Show Labels in %@", (_usingLocaleBasedCountryLabels ? @"Default Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])],
+ @"Show Snapshots"
]];
if (self.debugLoggingEnabled)
@@ -403,81 +406,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsCoreRenderingOverdrawVisualization:
self.mapView.debugMask ^= MGLMapDebugOverdrawVisualizationMask;
break;
- case MBXSettingsCoreRenderingToggleTwoMaps:
- if ([self.view viewWithTag:2] == nil) {
- MGLMapView *secondMapView = [[MGLMapView alloc] initWithFrame:
- CGRectMake(0, self.view.bounds.size.height / 2,
- self.view.bounds.size.width, self.view.bounds.size.height / 2)];
- secondMapView.translatesAutoresizingMaskIntoConstraints = false;
- secondMapView.tag = 2;
- for (NSLayoutConstraint *constraint in self.view.constraints)
- {
- if ((constraint.firstItem == self.mapView && constraint.firstAttribute == NSLayoutAttributeBottom) ||
- (constraint.secondItem == self.mapView && constraint.secondAttribute == NSLayoutAttributeBottom))
- {
- [self.view removeConstraint:constraint];
- break;
- }
- }
- [self.view addSubview:secondMapView];
- [self.view addConstraints:@[
- [NSLayoutConstraint constraintWithItem:self.mapView
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeCenterY
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeCenterX
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeCenterX
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeWidth
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeWidth
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeCenterY
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationEqual
- toItem:self.bottomLayoutGuide
- attribute:NSLayoutAttributeTop
- multiplier:1
- constant:0],
- ]];
- } else {
- NSMutableArray *constraintsToRemove = [NSMutableArray array];
- MGLMapView *secondMapView = (MGLMapView *)[self.view viewWithTag:2];
- for (NSLayoutConstraint *constraint in self.view.constraints)
- {
- if (constraint.firstItem == secondMapView || constraint.secondItem == secondMapView)
- {
- [constraintsToRemove addObject:constraint];
- }
- }
- [self.view removeConstraints:constraintsToRemove];
- [secondMapView removeFromSuperview];
- [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.mapView
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationEqual
- toItem:self.bottomLayoutGuide
- attribute:NSLayoutAttributeTop
- multiplier:1
- constant:0]];
- }
- break;
default:
NSAssert(NO, @"All core rendering setting rows should be implemented");
break;
@@ -516,6 +444,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsAnnotationsQueryAnnotations:
[self testQueryPointAnnotations];
break;
+ case MBXSettingsAnnotationsCustomUserDot:
+ [self toggleCustomUserDot];
+ break;
case MBXSettingsAnnotationsRemoveAnnotations:
[self.mapView removeAnnotations:self.mapView.annotations];
break;
@@ -590,9 +521,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsRuntimeStylingImageSource:
[self styleImageSource];
break;
- case MBXSettingsRuntimeStylingCountryLabels:
- [self styleCountryLabelsLanguage];
- break;
case MBXSettingsRuntimeStylingRouteLine:
[self styleRouteLine];
break;
@@ -607,12 +535,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsMiscellaneous:
switch (indexPath.row)
{
+ case MBXSettingsMiscellaneousCountryLabels:
+ [self styleCountryLabelsLanguage];
+ break;
case MBXSettingsMiscellaneousWorldTour:
[self startWorldTour];
break;
- case MBXSettingsMiscellaneousCustomUserDot:
- [self toggleCustomUserDot];
- break;
case MBXSettingsMiscellaneousPrintLogFile:
[self printTelemetryLogFile];
break;
@@ -624,6 +552,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
self.reuseQueueStatsEnabled = !self.reuseQueueStatsEnabled;
self.hudLabel.hidden = !self.reuseQueueStatsEnabled;
self.showZoomLevelEnabled = NO;
+ [self updateHUD];
break;
}
case MBXSettingsMiscellaneousShowZoomLevel:
@@ -631,6 +560,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
self.showZoomLevelEnabled = !self.showZoomLevelEnabled;
self.hudLabel.hidden = !self.showZoomLevelEnabled;
self.reuseQueueStatsEnabled = NO;
+ [self updateHUD];
break;
}
case MBXSettingsMiscellaneousScrollView:
@@ -640,6 +570,86 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.navigationController pushViewController:embeddedMapViewController animated:YES];
break;
}
+ case MBXSettingsMiscellaneousToggleTwoMaps:
+ if ([self.view viewWithTag:2] == nil) {
+ MGLMapView *secondMapView = [[MGLMapView alloc] initWithFrame:
+ CGRectMake(0, self.view.bounds.size.height / 2,
+ self.view.bounds.size.width, self.view.bounds.size.height / 2)];
+ secondMapView.translatesAutoresizingMaskIntoConstraints = false;
+ secondMapView.tag = 2;
+ for (NSLayoutConstraint *constraint in self.view.constraints)
+ {
+ if ((constraint.firstItem == self.mapView && constraint.firstAttribute == NSLayoutAttributeBottom) ||
+ (constraint.secondItem == self.mapView && constraint.secondAttribute == NSLayoutAttributeBottom))
+ {
+ [self.view removeConstraint:constraint];
+ break;
+ }
+ }
+ [self.view addSubview:secondMapView];
+ [self.view addConstraints:@[
+ [NSLayoutConstraint constraintWithItem:self.mapView
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeCenterY
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeCenterX
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeCenterX
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeWidth
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeCenterY
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ multiplier:1
+ constant:0],
+ ]];
+ } else {
+ NSMutableArray *constraintsToRemove = [NSMutableArray array];
+ MGLMapView *secondMapView = (MGLMapView *)[self.view viewWithTag:2];
+ for (NSLayoutConstraint *constraint in self.view.constraints)
+ {
+ if (constraint.firstItem == secondMapView || constraint.secondItem == secondMapView)
+ {
+ [constraintsToRemove addObject:constraint];
+ }
+ }
+ [self.view removeConstraints:constraintsToRemove];
+ [secondMapView removeFromSuperview];
+ [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.mapView
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ multiplier:1
+ constant:0]];
+ }
+ break;
+ case MBXSettingsMiscellaneousShowSnapshots:
+ {
+ [self performSegueWithIdentifier:@"ShowSnapshots" sender:nil];
+ break;
+ }
default:
NSAssert(NO, @"All miscellaneous setting rows should be implemented");
break;
@@ -1303,8 +1313,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)styleRasterSource
{
- NSArray *tileURLTemplates = @[@"https://stamen-tiles.a.ssl.fastly.net/terrain-background/{z}/{x}/{y}.jpg"];
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"style-raster-source-id" tileURLTemplates:tileURLTemplates options:@{
+ NSString *tileURL = [NSString stringWithFormat:@"https://stamen-tiles.a.ssl.fastly.net/terrain-background/{z}/{x}/{y}%@.jpg", UIScreen.mainScreen.nativeScale > 1 ? @"@2x" : @""];
+ MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"style-raster-source-id" tileURLTemplates:@[tileURL] options:@{
MGLTileSourceOptionTileSize: @256,
}];
[self.mapView.style addSource:rasterSource];
@@ -1348,12 +1358,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
-(void)styleCountryLabelsLanguage
{
- NSArray<NSString *> *labelLayers = @[
- @"country-label-lg",
- @"country-label-md",
- @"country-label-sm",
- ];
- [self styleLabelLanguageForLayersNamed:labelLayers];
+ _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
+ self.mapView.style.localizesLabels = _usingLocaleBasedCountryLabels;
}
- (void)styleRouteLine
@@ -1436,39 +1442,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addLayer:fillStyleLayer];
}
-- (void)styleLabelLanguageForLayersNamed:(NSArray<NSString *> *)layers
-{
- _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
- NSString *bestLanguageForUser = [NSString stringWithFormat:@"{name_%@}", [self bestLanguageForUser]];
- NSString *language = _usingLocaleBasedCountryLabels ? bestLanguageForUser : @"{name}";
-
- for (NSString *layerName in layers) {
- MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:layerName];
-
- if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
- if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
- MGLConstantStyleValue *label = (MGLConstantStyleValue<NSString *> *)layer.text;
- if ([label.rawValue hasPrefix:@"{name"]) {
- layer.text = [MGLStyleValue valueWithRawValue:language];
- }
- }
- else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
- NSMutableDictionary *stops = function.stops.mutableCopy;
- [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) {
- if ([stop.rawValue hasPrefix:@"{name"]) {
- stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:language];
- }
- }];
- function.stops = stops;
- layer.text = function;
- }
- } else {
- NSLog(@"%@ is not a symbol style layer", layerName);
- }
- }
-}
-
- (NSString *)bestLanguageForUser
{
// https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
@@ -1628,8 +1601,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[MGLStyle darkStyleURL],
[MGLStyle satelliteStyleURL],
[MGLStyle satelliteStreetsStyleURL],
- [MGLStyle trafficDayStyleURL],
- [MGLStyle trafficNightStyleURL],
+ [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"],
+ [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"],
+
];
NSAssert(styleNames.count == styleURLs.count, @"Style names and URLs don’t match.");
@@ -1686,7 +1660,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[sender setAccessibilityValue:nextAccessibilityValue];
}
-#pragma mark - Map Delegate
+#pragma mark - MGLMapViewDelegate
- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation
{
@@ -1904,22 +1878,33 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)mapViewRegionIsChanging:(MGLMapView *)mapView
{
+ [self updateHUD];
+}
+
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
+ [self updateHUD];
+}
+
+- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(MGLUserLocation *)userLocation {
+ [self updateHUD];
+}
+
+- (void)updateHUD {
+ if (!self.reuseQueueStatsEnabled && !self.showZoomLevelEnabled) return;
+
+ NSString *hudString;
+
if (self.reuseQueueStatsEnabled) {
NSUInteger queuedAnnotations = 0;
- for (NSArray *queue in self.mapView.annotationViewReuseQueueByIdentifier.allValues)
- {
+ for (NSArray *queue in self.mapView.annotationViewReuseQueueByIdentifier.allValues) {
queuedAnnotations += queue.count;
}
- self.hudLabel.text = [NSString stringWithFormat:@" Visible: %ld Queued: %ld", (unsigned long)mapView.visibleAnnotations.count, (unsigned long)queuedAnnotations];
+ hudString = [NSString stringWithFormat:@"Visible: %ld Queued: %ld", (unsigned long)self.mapView.visibleAnnotations.count, (unsigned long)queuedAnnotations];
} else if (self.showZoomLevelEnabled) {
- self.hudLabel.text = [NSString stringWithFormat:@" Zoom: %.2f", self.mapView.zoomLevel];
+ hudString = [NSString stringWithFormat:@"%.2f ∕ ↕\U0000FE0E%.f° ∕ %.f°", self.mapView.zoomLevel, self.mapView.camera.pitch, self.mapView.direction];
}
-}
-- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
- if (self.showZoomLevelEnabled) {
- self.hudLabel.text = [NSString stringWithFormat:@" Zoom: %.2f", self.mapView.zoomLevel];
- }
+ [self.hudLabel setTitle:hudString forState:UIControlStateNormal];
}
@end
diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard
index 40198146ab..507582213f 100644
--- a/platform/ios/app/Main.storyboard
+++ b/platform/ios/app/Main.storyboard
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="PSe-Ot-7Ff">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G8c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="PSe-Ot-7Ff">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
@@ -33,17 +33,24 @@
<outletCollection property="gestureRecognizers" destination="lfd-mn-7en" appends="YES" id="0PH-gH-GRm"/>
</connections>
</view>
- <label opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="58y-pX-YyB">
- <rect key="frame" x="179" y="626" width="180" height="21"/>
+ <button opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="58y-pX-YyB">
+ <rect key="frame" x="319" y="606" width="40" height="21"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <accessibility key="accessibilityConfiguration">
+ <accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
+ </accessibility>
<constraints>
- <constraint firstAttribute="width" constant="180" id="OL2-l5-I2f"/>
- <constraint firstAttribute="height" constant="21" id="xHg-ye-wzT"/>
+ <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="40" id="OL2-l5-I2f"/>
+ <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="xHg-ye-wzT"/>
</constraints>
- <fontDescription key="fontDescription" type="system" pointSize="8"/>
- <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <nil key="highlightedColor"/>
- </label>
+ <fontDescription key="fontDescription" type="system" pointSize="10"/>
+ <inset key="contentEdgeInsets" minX="4" minY="2" maxX="4" maxY="2"/>
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+ <integer key="value" value="2"/>
+ </userDefinedRuntimeAttribute>
+ </userDefinedRuntimeAttributes>
+ </button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
@@ -51,7 +58,8 @@
<constraint firstItem="kNe-zV-9ha" firstAttribute="bottom" secondItem="m8o-i7-QIy" secondAttribute="top" id="Etp-BC-E1N"/>
<constraint firstAttribute="trailing" secondItem="kNe-zV-9ha" secondAttribute="trailing" id="MGr-8G-VEb"/>
<constraint firstItem="58y-pX-YyB" firstAttribute="trailing" secondItem="Z9X-fc-PUC" secondAttribute="trailingMargin" id="O3a-bR-boI"/>
- <constraint firstItem="m8o-i7-QIy" firstAttribute="top" secondItem="58y-pX-YyB" secondAttribute="bottom" constant="20" id="cjh-ZS-Mv4"/>
+ <constraint firstItem="58y-pX-YyB" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Z9X-fc-PUC" secondAttribute="leadingMargin" id="ceH-yz-ewY"/>
+ <constraint firstItem="m8o-i7-QIy" firstAttribute="top" secondItem="58y-pX-YyB" secondAttribute="bottom" constant="40" id="cjh-ZS-Mv4"/>
<constraint firstItem="kNe-zV-9ha" firstAttribute="top" secondItem="Z9X-fc-PUC" secondAttribute="top" id="qMm-e9-jxH"/>
</constraints>
</view>
@@ -66,7 +74,7 @@
</connections>
</barButtonItem>
<button key="titleView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="KsN-ny-Hou">
- <rect key="frame" x="61" y="7" width="207" height="30"/>
+ <rect key="frame" x="65" y="5.5" width="203" height="33"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<state key="normal" title="Streets"/>
@@ -94,8 +102,9 @@
</rightBarButtonItems>
</navigationItem>
<connections>
- <outlet property="hudLabel" destination="58y-pX-YyB" id="MEh-ir-3IH"/>
+ <outlet property="hudLabel" destination="58y-pX-YyB" id="aGG-7a-bZR"/>
<outlet property="mapView" destination="kNe-zV-9ha" id="VNR-WO-1q4"/>
+ <segue destination="zvf-Qd-4Ru" kind="show" identifier="ShowSnapshots" id="hzX-Jp-UJq"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="AAd-8J-9UU" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -352,6 +361,81 @@
</objects>
<point key="canvasLocation" x="594.39999999999998" y="1083.5082458770617"/>
</scene>
+ <!--Snapshots View Controller-->
+ <scene sceneID="Ooh-2U-4Bz">
+ <objects>
+ <viewController id="zvf-Qd-4Ru" customClass="MBXSnapshotsViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="ZLI-ej-4Bs"/>
+ <viewControllerLayoutGuide type="bottom" id="fiS-dq-r4S"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Jxm-v6-zI0">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="miZ-Fw-EWq" userLabel="Image View TL">
+ <rect key="frame" x="0.0" y="64" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="XuN-T4-Z83" userLabel="Image View TM">
+ <rect key="frame" x="125" y="64" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ykR-Ku-i9l" userLabel="Image View TR">
+ <rect key="frame" x="250" y="64" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TL0-V8-T2F" userLabel="Image View BL">
+ <rect key="frame" x="0.0" y="365.5" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="eMy-JU-rq4" userLabel="Image View BM">
+ <rect key="frame" x="125" y="365.5" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="zT0-3J-0xw" userLabel="Image View BR">
+ <rect key="frame" x="250" y="365.5" width="125" height="301.5"/>
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <constraints>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="leading" secondItem="TL0-V8-T2F" secondAttribute="trailing" id="0xP-ii-cyV"/>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="top" secondItem="XuN-T4-Z83" secondAttribute="bottom" id="1HV-Tp-mUB"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="leading" secondItem="Jxm-v6-zI0" secondAttribute="leading" id="3fH-bn-5ND"/>
+ <constraint firstItem="miZ-Fw-EWq" firstAttribute="leading" secondItem="Jxm-v6-zI0" secondAttribute="leading" id="4yV-CW-c5n"/>
+ <constraint firstItem="fiS-dq-r4S" firstAttribute="top" secondItem="eMy-JU-rq4" secondAttribute="bottom" id="57P-Qo-M11"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="top" secondItem="ZLI-ej-4Bs" secondAttribute="bottom" id="ARo-Nk-uVV"/>
+ <constraint firstAttribute="trailing" secondItem="ykR-Ku-i9l" secondAttribute="trailing" id="BRi-93-PGb"/>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="FqJ-zb-pkb"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="GrM-9L-dba"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="HSd-2T-Kz7"/>
+ <constraint firstAttribute="trailing" secondItem="zT0-3J-0xw" secondAttribute="trailing" id="HaC-la-079"/>
+ <constraint firstItem="fiS-dq-r4S" firstAttribute="top" secondItem="TL0-V8-T2F" secondAttribute="bottom" id="JgE-s8-RAh"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="top" secondItem="ykR-Ku-i9l" secondAttribute="bottom" id="KQm-ue-i3z"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="LUI-BF-66V"/>
+ <constraint firstItem="fiS-dq-r4S" firstAttribute="top" secondItem="zT0-3J-0xw" secondAttribute="bottom" id="MAe-3N-78O"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="OvH-2m-yli"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="top" secondItem="ZLI-ej-4Bs" secondAttribute="bottom" id="bzY-6Y-K80"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="leading" secondItem="miZ-Fw-EWq" secondAttribute="trailing" id="jhf-gz-4UF"/>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="l3m-tf-b1h"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="leading" secondItem="XuN-T4-Z83" secondAttribute="trailing" id="oEV-Yi-iLs"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="top" secondItem="miZ-Fw-EWq" secondAttribute="bottom" id="oLW-zh-Fnk"/>
+ <constraint firstItem="miZ-Fw-EWq" firstAttribute="top" secondItem="ZLI-ej-4Bs" secondAttribute="bottom" id="qpD-mN-wfP"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="sP4-HJ-Vgk"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="sTw-zD-Jid"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="t0u-eQ-Ail"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="uQU-pB-kvq"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="leading" secondItem="eMy-JU-rq4" secondAttribute="trailing" id="w8M-MN-cmx"/>
+ </constraints>
+ </view>
+ <connections>
+ <outlet property="snapshotImageViewBL" destination="TL0-V8-T2F" id="e6C-dB-kHm"/>
+ <outlet property="snapshotImageViewBM" destination="eMy-JU-rq4" id="zeR-3U-EbH"/>
+ <outlet property="snapshotImageViewBR" destination="zT0-3J-0xw" id="6YR-lR-ela"/>
+ <outlet property="snapshotImageViewTL" destination="miZ-Fw-EWq" id="2Jj-kh-3Zw"/>
+ <outlet property="snapshotImageViewTM" destination="XuN-T4-Z83" id="MXY-7F-jB2"/>
+ <outlet property="snapshotImageViewTR" destination="ykR-Ku-i9l" id="aEL-Sg-RIW"/>
+ </connections>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="5xV-Ua-pqK" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="1365.5999999999999" y="1083.5082458770617"/>
+ </scene>
</scenes>
<resources>
<image name="TrackingLocationOffMask.png" width="23" height="23"/>
diff --git a/platform/ios/bitrise.yml b/platform/ios/bitrise.yml
index 53287ec06d..36adbf7a77 100644
--- a/platform/ios/bitrise.yml
+++ b/platform/ios/bitrise.yml
@@ -17,8 +17,6 @@ workflows:
#!/bin/bash
set -eu -o pipefail
brew install cmake
- brew tap mapbox/homebrew-ios-sim-3
- brew install mapbox/homebrew-ios-sim-3/ios-sim
- is_debug: 'yes'
- script:
title: Generate Workspace
diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake
index 7ea1dddef7..c3db194988 100644
--- a/platform/ios/config.cmake
+++ b/platform/ios/config.cmake
@@ -2,37 +2,29 @@ add_definitions(-DMBGL_USE_GLES2=1)
mason_use(icu VERSION 58.1-min-size)
-macro(mbgl_platform_core)
- set_xcode_property(mbgl-core IPHONEOS_DEPLOYMENT_TARGET "8.0")
- set_xcode_property(mbgl-core ENABLE_BITCODE "YES")
- set_xcode_property(mbgl-core BITCODE_GENERATION_MODE bitcode)
- set_xcode_property(mbgl-core ONLY_ACTIVE_ARCH $<$<CONFIG:Debug>:YES>)
+macro(initialize_ios_target target)
+ set_xcode_property(${target} IPHONEOS_DEPLOYMENT_TARGET "8.0")
+ set_xcode_property(${target} ENABLE_BITCODE "YES")
+ set_xcode_property(${target} BITCODE_GENERATION_MODE bitcode)
+ set_xcode_property(${target} ONLY_ACTIVE_ARCH $<$<CONFIG:Debug>:YES>)
- target_sources(mbgl-core
- # Loop
- PRIVATE platform/darwin/src/async_task.cpp
- PRIVATE platform/darwin/src/run_loop.cpp
- PRIVATE platform/darwin/src/timer.cpp
+ target_compile_options(${target}
+ PRIVATE -fobjc-arc
+ )
+endmacro()
- # File source
- PRIVATE platform/darwin/src/http_file_source.mm
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
+include(cmake/loop-darwin.cmake)
+initialize_ios_target(mbgl-loop-darwin)
+
+
+macro(mbgl_platform_core)
+ initialize_ios_target(mbgl-core)
+
+ target_sources(mbgl-core
# Default styles
PRIVATE platform/default/mbgl/util/default_styles.hpp
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
-
# Misc
PRIVATE platform/darwin/mbgl/storage/reachability.h
PRIVATE platform/darwin/mbgl/storage/reachability.m
@@ -57,6 +49,10 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/gl/headless_display.cpp
PRIVATE platform/default/mbgl/gl/headless_display.hpp
+ # Snapshotting
+ PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.hpp
+
# Thread pool
PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp
@@ -69,7 +65,6 @@ macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PRIVATE icu)
target_compile_options(mbgl-core
- PRIVATE -fobjc-arc
PRIVATE -fvisibility=hidden
)
@@ -93,6 +88,27 @@ macro(mbgl_platform_core)
PUBLIC "-framework ImageIO"
PUBLIC "-framework MobileCoreServices"
PUBLIC "-framework SystemConfiguration"
+ )
+endmacro()
+
+
+macro(mbgl_filesource)
+ initialize_ios_target(mbgl-filesource)
+
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/darwin/src/http_file_source.mm
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_compile_options(mbgl-filesource
+ PRIVATE -fvisibility=hidden
+ )
+
+ target_link_libraries(mbgl-filesource
PUBLIC "-lsqlite3"
+ PUBLIC "-framework Foundation"
)
endmacro()
diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md
index 6c2693cbbf..ebad32e04c 100644
--- a/platform/ios/docs/doc-README.md
+++ b/platform/ios/docs/doc-README.md
@@ -6,4 +6,4 @@ The Mapbox iOS SDK is an open-source framework for embedding interactive map vie
For setup information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/). For detailed usage instructions, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/). A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) is also available.
-If you have any questions, please [contact our support team](https://www.mapbox.com/contact/). We welcome your [bug reports and feature requests](https://github.com/mapbox/mapbox-gl-native/issues/).
+If you have any questions, please see [our help page](https://www.mapbox.com/help/). We welcome your [bug reports, feature requests, and contributions](https://github.com/mapbox/mapbox-gl-native/issues/).
diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md
index 0a7edc5a41..2e5a78841c 100644
--- a/platform/ios/docs/pod-README.md
+++ b/platform/ios/docs/pod-README.md
@@ -96,4 +96,4 @@ class ViewController: UIViewController {
Full API documentation is included in this package, within the `documentation` folder. For more details, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/).
-If you have any questions, please [contact our support team](https://www.mapbox.com/contact/). We welcome your [bug reports and feature requests](https://github.com/mapbox/mapbox-gl-native/issues/).
+If you have any questions, please see [our help page](https://www.mapbox.com/help/). We welcome your [bug reports, feature requests, and contributions](https://github.com/mapbox/mapbox-gl-native/issues/).
diff --git a/platform/ios/framework/Settings.bundle/hu.lproj/Root.strings b/platform/ios/framework/Settings.bundle/hu.lproj/Root.strings
new file mode 100644
index 0000000000..3d761f2b97
--- /dev/null
+++ b/platform/ios/framework/Settings.bundle/hu.lproj/Root.strings
@@ -0,0 +1,3 @@
+"TELEMETRY_GROUP_TITLE" = "Adatvédelmi beállítások";
+"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetria";
+"TELEMETRY_GROUP_FOOTER" = "Ez a beállítás megengedi az alkalmazásnak, hogy névtelen helyzeti és használati adatokat osszon meg a Mapbox-szal.";
diff --git a/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings b/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings
index 13a8d57020..c6dc565d50 100644
--- a/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings
@@ -1,3 +1,3 @@
"TELEMETRY_GROUP_TITLE" = "隱私設置";
-"TELEMETRY_SWITCH_TITLE" = "Mapbox傳感數據";
-"TELEMETRY_GROUP_FOOTER" = "此設置允許應用將用戶位置和數據以匿名的方式分享給Mapbox。";
+"TELEMETRY_SWITCH_TITLE" = "Mapbox遙測";
+"TELEMETRY_GROUP_FOOTER" = "此設置允許應用程式將位置資訊及使用數據以匿名的方式分享給Mapbox。";
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index f83aa50509..a3ccc9337b 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -22,6 +22,10 @@
1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454941ECD450D00021D39 /* MGLLight_Private.h */; };
1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */; };
1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */; };
+ 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */; };
+ 1FB7DAB01F2A4DC200410606 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */; };
+ 1FB7DAB11F2A4DC800410606 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */; };
+ 1FB7DAB21F2A4DC900410606 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */; };
30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; };
30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; };
30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */; };
@@ -149,7 +153,6 @@
400533011DB0862B0069F638 /* NSArray+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */; };
400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */; };
400533031DB086490069F638 /* NSArray+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */; };
- 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */; };
4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -201,6 +204,10 @@
558DE7A11E5615E400C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */; };
558DE7A21E5615E400C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE79F1E5615E400C7916D /* MGLFoundation.mm */; };
558DE7A31E5615E400C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE79F1E5615E400C7916D /* MGLFoundation.mm */; };
+ 55D120A61F791007004B6D81 /* libmbgl-loop-darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */; };
+ 55D120A81F79100C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */; };
+ 55D120AA1F791015004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */; };
+ 55D120AC1F791018004B6D81 /* libmbgl-loop-darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */; };
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */; };
632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 632281DE1E6F855900D75A5D /* MBXEmbeddedMapViewController.m */; };
6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */; };
@@ -213,6 +220,12 @@
7E016D861D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; };
7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */; };
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */; };
+ 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */; };
+ 927FBCFF1F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 927FBD011F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */; };
+ 927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */; };
+ 929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
92F2C3ED1F0E3C3A00268EC0 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */; };
960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
@@ -220,8 +233,15 @@
9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; };
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
+ 966FCF4C1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */; };
+ 966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */; };
+ 966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */; };
+ 966FCF531F3C322400F2B6DE /* MGLUserLocationHeadingArrowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */; };
+ 966FCF541F3C323300F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */; };
+ 966FCF551F3C323500F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */; };
968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3557F7AE1E1D27D300CCA5E6 /* MGLDistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
96E027231E57C76E004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027251E57C76E004B8E66 /* Localizable.strings */; };
+ 96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; };
DA00FC8E1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */; };
@@ -237,7 +257,6 @@
DA1DC9971CB6E046006E619F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9961CB6E046006E619F /* main.m */; };
DA1DC9991CB6E054006E619F /* MBXAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */; };
DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC99A1CB6E064006E619F /* MBXViewController.m */; };
- DA1DC99D1CB6E076006E619F /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */; };
DA1DC99F1CB6E088006E619F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC99E1CB6E088006E619F /* Assets.xcassets */; };
DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */; };
DA2207BF1DC0805F0002F84D /* MGLStyleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */; };
@@ -558,6 +577,8 @@
1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; };
1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = "<group>"; };
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; };
+ 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MGLAdditions.h"; sourceTree = "<group>"; };
+ 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MGLAdditions.m"; sourceTree = "<group>"; };
20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = "<group>"; };
20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
20DABE8A1DF78149007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Root.strings"; sourceTree = "<group>"; };
@@ -673,6 +694,10 @@
556660D71E1D085500E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = "<group>"; };
558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = "<group>"; };
558DE79F1E5615E400C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = "<group>"; };
+ 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
55D8C9941D0F133500F42F10 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/ios/config.xcconfig; sourceTree = "<group>"; };
55D8C9951D0F18CE00F42F10 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = "<group>"; };
@@ -684,6 +709,10 @@
7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLPolygon+MGLAdditions.h"; sourceTree = "<group>"; };
7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLPolygon+MGLAdditions.m"; sourceTree = "<group>"; };
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; };
+ 927FBCFA1F4DAA8300F8BF1F /* MBXSnapshotsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXSnapshotsViewController.h; sourceTree = "<group>"; };
+ 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXSnapshotsViewController.m; sourceTree = "<group>"; };
+ 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; };
+ 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; };
92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererFrontend.h; sourceTree = "<group>"; };
960D0C351ECF5AAF008E151F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSDKUpdateChecker.h; sourceTree = "<group>"; };
@@ -693,6 +722,10 @@
9660916D1E5BBFDB00A9A03B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916E1E5BBFDC00A9A03B /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916F1E5BBFDE00A9A03B /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingBeamLayer.h; sourceTree = "<group>"; };
+ 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationHeadingBeamLayer.m; sourceTree = "<group>"; };
+ 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingArrowLayer.h; sourceTree = "<group>"; };
+ 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationHeadingArrowLayer.m; sourceTree = "<group>"; };
968F36B41E4D0FC6003A5522 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
96E027241E57C76E004B8E66 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
96E027271E57C77A004B8E66 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -703,6 +736,7 @@
96E0272C1E57C7E5004B8E66 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
96E0272D1E57C7E6004B8E66 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
96E0272E1E57C7E7004B8E66 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
+ 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = "<group>"; };
DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = "<group>"; };
DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = "<group>"; };
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = "<group>"; };
@@ -724,7 +758,6 @@
DA1DC9961CB6E046006E619F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAppDelegate.m; sourceTree = "<group>"; };
DA1DC99A1CB6E064006E619F /* MBXViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXViewController.m; sourceTree = "<group>"; };
- DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
DA1DC99E1CB6E088006E619F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationGuideTests.swift; path = ../../darwin/test/MGLDocumentationGuideTests.swift; sourceTree = "<group>"; };
DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLStyleValueTests.swift; path = ../../darwin/test/MGLStyleValueTests.swift; sourceTree = "<group>"; };
@@ -767,7 +800,7 @@
DA618B191E68883700CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ca; path = ca.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA618B1A1E68883900CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B1B1E68884E00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
- DA618B1C1E6888EC00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA618B1C1E6888EC00CB7F44 /* ca */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Foundation.strings; sourceTree = "<group>"; };
DA618B1D1E6888F500CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ca; path = ca.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B1E1E688A3700CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Root.strings; sourceTree = "<group>"; };
DA618B251E68920500CB7F44 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Foundation.strings; sourceTree = "<group>"; };
@@ -776,6 +809,10 @@
DA618B2C1E68933600CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Root.strings; sourceTree = "<group>"; };
DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = "<group>"; };
DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = "<group>"; };
+ DA704CBB1F637311004B3F28 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA704CBC1F637405004B3F28 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DA704CBD1F63746E004B3F28 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
+ DA704CC71F6663A3004B3F28 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Foundation.strings; sourceTree = "<group>"; };
DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOpenGLStyleLayer.h; sourceTree = "<group>"; };
DA72620A1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOpenGLStyleLayer.mm; sourceTree = "<group>"; };
DA737ADA1E59139D00AD2CDE /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
@@ -891,7 +928,7 @@
DAA32CAC1E4C4971006F8D24 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CB11E4C4C8A006F8D24 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Root.strings; sourceTree = "<group>"; };
DAA32CB51E4C4CF4006F8D24 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Foundation.strings; sourceTree = "<group>"; };
- DAA32CB71E4C4ED8006F8D24 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DAA32CB71E4C4ED8006F8D24 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CB81E4C4EE6006F8D24 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Root.strings; sourceTree = "<group>"; };
DAA32CBC1E4C4F5D006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CBD1E4C4F62006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Foundation.strings; sourceTree = "<group>"; };
@@ -915,6 +952,8 @@
DABCABBF1CB80717000A7C39 /* locations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = locations.cpp; sourceTree = "<group>"; };
DABCABC01CB80717000A7C39 /* locations.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = locations.hpp; sourceTree = "<group>"; };
DAC49C621CD07D74009E1AA3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DACCD9C81F1F473700BB09A1 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Root.strings; sourceTree = "<group>"; };
+ DACFE7981F66EA2100630DA8 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DAD165691CF41981001FF4B9 /* MGLFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature.h; sourceTree = "<group>"; };
DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature_Private.h; sourceTree = "<group>"; };
DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = "<group>"; };
@@ -930,8 +969,8 @@
DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterSource_Private.h; sourceTree = "<group>"; };
DAF0D8121DFE0EC500B28378 /* MGLVectorSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorSource_Private.h; sourceTree = "<group>"; };
DAF0D8171DFE6B2800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = "<group>"; };
- DAFBD0D21E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; };
- DAFBD0D31E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
+ DAFBD0D21E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; };
+ DAFBD0D31E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
DAFBD0D41E3FA7A2000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Root.strings"; sourceTree = "<group>"; };
DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = "<group>"; };
DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = "<group>"; };
@@ -970,6 +1009,8 @@
buildActionMask = 2147483647;
files = (
DAABF73D1CBC59BB005B1825 /* libmbgl-core.a in Frameworks */,
+ 55D120A61F791007004B6D81 /* libmbgl-loop-darwin.a in Frameworks */,
+ 55D120A81F79100C004B6D81 /* libmbgl-filesource.a in Frameworks */,
DA27C24E1CBB3811000B0ECD /* GLKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -979,6 +1020,8 @@
buildActionMask = 2147483647;
files = (
36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */,
+ 55D120AC1F791018004B6D81 /* libmbgl-loop-darwin.a in Frameworks */,
+ 55D120AA1F791015004B6D81 /* libmbgl-filesource.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1164,6 +1207,20 @@
name = Sources;
sourceTree = "<group>";
};
+ 9604FC341F313A5E003EEA02 /* Fixtures */ = {
+ isa = PBXGroup;
+ children = (
+ 353BAEF51D646370009A8DA9 /* amsterdam.geojson */,
+ DA1DC96C1CB6C6CE006E619F /* points.geojson */,
+ DA1DC96D1CB6C6CE006E619F /* polyline.geojson */,
+ DA1DC96F1CB6C6CE006E619F /* threestates.geojson */,
+ DD4823721D94AE6C00EB71B7 /* fill_filter_style.json */,
+ DD4823731D94AE6C00EB71B7 /* line_filter_style.json */,
+ DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */,
+ );
+ name = Fixtures;
+ sourceTree = "<group>";
+ };
DA1DC9411CB6C1C2006E619F = {
isa = PBXGroup;
children = (
@@ -1205,6 +1262,8 @@
354B839B1D2E9B48005D9406 /* MBXUserLocationAnnotationView.m */,
DA1DC9681CB6C6B7006E619F /* MBXOfflinePacksTableViewController.h */,
DA1DC9691CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m */,
+ 927FBCFA1F4DAA8300F8BF1F /* MBXSnapshotsViewController.h */,
+ 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */,
DA1DC9531CB6C1C2006E619F /* MBXViewController.h */,
DA1DC99A1CB6E064006E619F /* MBXViewController.m */,
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */,
@@ -1212,16 +1271,9 @@
DA821D051CCC6D59007508D4 /* Main.storyboard */,
DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */,
DA1DC99E1CB6E088006E619F /* Assets.xcassets */,
- DA1DC96C1CB6C6CE006E619F /* points.geojson */,
- DA1DC96D1CB6C6CE006E619F /* polyline.geojson */,
- DA1DC96F1CB6C6CE006E619F /* threestates.geojson */,
- 353BAEF51D646370009A8DA9 /* amsterdam.geojson */,
- DD4823721D94AE6C00EB71B7 /* fill_filter_style.json */,
- DD4823731D94AE6C00EB71B7 /* line_filter_style.json */,
- DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */,
DA1DC95E1CB6C1C2006E619F /* Info.plist */,
- DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */,
96E027251E57C76E004B8E66 /* Localizable.strings */,
+ 9604FC341F313A5E003EEA02 /* Fixtures */,
DA1DC94D1CB6C1C2006E619F /* Supporting Files */,
);
name = "Demo App";
@@ -1239,6 +1291,10 @@
DA1DC9921CB6DF24006E619F /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */,
+ 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */,
+ 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */,
+ 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */,
5549A0371EF1D86B00073113 /* libmbgl-core.a */,
36F1153B1D46080700878E1A /* libmbgl-core.a */,
36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */,
@@ -1335,6 +1391,8 @@
558DE79F1E5615E400C7916D /* MGLFoundation.mm */,
DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */,
DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */,
+ 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */,
+ 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */,
DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */,
DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */,
92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */,
@@ -1554,6 +1612,8 @@
DAD165831CF4CFED001FF4B9 /* Categories */ = {
isa = PBXGroup;
children = (
+ 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */,
+ 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */,
7E016D821D9E890300A29A21 /* MGLPolygon+MGLAdditions.h */,
7E016D831D9E890300A29A21 /* MGLPolygon+MGLAdditions.m */,
7E016D7C1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h */,
@@ -1592,8 +1652,8 @@
DAD165841CF4D06B001FF4B9 /* Annotations */ = {
isa = PBXGroup;
children = (
- 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */,
404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */,
+ 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */,
40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */,
DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */,
DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */,
@@ -1612,6 +1672,11 @@
359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */,
354B83941D2E873E005D9406 /* MGLUserLocationAnnotationView.h */,
354B83951D2E873E005D9406 /* MGLUserLocationAnnotationView.m */,
+ 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */,
+ 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */,
+ 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */,
+ 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */,
+ 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */,
);
name = Annotations;
sourceTree = "<group>";
@@ -1659,6 +1724,7 @@
350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */,
404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */,
+ 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */,
DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */,
35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */,
3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
@@ -1667,6 +1733,7 @@
DA8847FB1CBAFA5100AB86E3 /* MGLShape.h in Headers */,
353933F51D3FB785003F57D7 /* MGLBackgroundStyleLayer.h in Headers */,
DA88485A1CBAFB9800AB86E3 /* MGLUserLocation_Private.h in Headers */,
+ 966FCF531F3C322400F2B6DE /* MGLUserLocationHeadingArrowLayer.h in Headers */,
DA27C24F1CBB4C11000B0ECD /* MGLAccountManager_Private.h in Headers */,
DA8847FC1CBAFA5100AB86E3 /* MGLStyle.h in Headers */,
DD9BE4F71EB263C50079A3AF /* UIViewController+MGLAdditions.h in Headers */,
@@ -1698,9 +1765,11 @@
DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */,
DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */,
DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */,
+ 96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */,
408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */,
DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */,
558DE7A01E5615E400C7916D /* MGLFoundation_Private.h in Headers */,
+ 966FCF4C1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h in Headers */,
DA88483D1CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h in Headers */,
DA17BE301CC4BAC300402C41 /* MGLMapView_Private.h in Headers */,
DAD165781CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */,
@@ -1709,6 +1778,7 @@
3566C7661D4A77BA008152BC /* MGLShapeSource.h in Headers */,
35CE61821D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */,
35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */,
+ 927FBCFF1F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */,
DA35A29E1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
DAF0D8181DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */,
DAAF722B1DA903C700312FA4 /* MGLStyleValue.h in Headers */,
@@ -1785,6 +1855,7 @@
35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */,
DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */,
DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */,
+ 927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */,
3566C7721D4A9198008152BC /* MGLSource_Private.h in Headers */,
353933FF1D3FB7DD003F57D7 /* MGLSymbolStyleLayer.h in Headers */,
DAAF722E1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */,
@@ -1793,7 +1864,6 @@
DABFB8621CBE99E500D62B32 /* MGLOfflinePack.h in Headers */,
DAD1656D1CF41981001FF4B9 /* MGLFeature.h in Headers */,
DA17BE311CC4BDAA00402C41 /* MGLMapView_Private.h in Headers */,
- 92F2C3EE1F0E3DC600268EC0 /* MGLRendererFrontend.h in Headers */,
DABFB86C1CBE99E500D62B32 /* MGLTypes.h in Headers */,
DABFB8691CBE99E500D62B32 /* MGLShape.h in Headers */,
9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */,
@@ -1840,6 +1910,7 @@
35136D4D1D4277FC00C20EFD /* MGLSource.h in Headers */,
DA35A2BC1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */,
35D13AC41D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */,
+ 1FB7DAB01F2A4DC200410606 /* MGLVectorSource+MGLAdditions.h in Headers */,
DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */,
1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */,
DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */,
@@ -2066,7 +2137,6 @@
353BAEF61D646370009A8DA9 /* amsterdam.geojson in Resources */,
DA1DC9711CB6C6CE006E619F /* polyline.geojson in Resources */,
DD4823761D94AE6C00EB71B7 /* line_filter_style.json in Resources */,
- DA1DC99D1CB6E076006E619F /* Default-568h@2x.png in Resources */,
DA821D071CCC6D59007508D4 /* Main.storyboard in Resources */,
DA1DC9731CB6C6CE006E619F /* threestates.geojson in Resources */,
DA821D061CCC6D59007508D4 /* LaunchScreen.storyboard in Resources */,
@@ -2155,6 +2225,7 @@
DA1DC9991CB6E054006E619F /* MBXAppDelegate.m in Sources */,
DA1DC96B1CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m in Sources */,
DA1DC96A1CB6C6B7006E619F /* MBXCustomCalloutView.m in Sources */,
+ 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */,
DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */,
40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */,
632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */,
@@ -2218,11 +2289,13 @@
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
354B83981D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */,
DA88485D1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.m in Sources */,
+ 1FB7DAB11F2A4DC800410606 /* MGLVectorSource+MGLAdditions.m in Sources */,
DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */,
30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */,
40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */,
DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */,
DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.mm in Sources */,
+ 929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */,
35136D3C1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */,
DD9BE4F81EB263C50079A3AF /* UIViewController+MGLAdditions.m in Sources */,
350098DE1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */,
@@ -2233,7 +2306,9 @@
3538AA1F1D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
DA88482D1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m in Sources */,
+ 966FCF541F3C323300F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */,
DA88485B1CBAFB9800AB86E3 /* MGLUserLocation.m in Sources */,
+ 927FBD011F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */,
350098BD1D480108004B2AF0 /* MGLVectorSource.mm in Sources */,
3566C76E1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */,
DA88488C1CBB037E00AB86E3 /* SMCalloutView.m in Sources */,
@@ -2264,7 +2339,6 @@
3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */,
DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
- 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */,
404C26E41D89B877000AA13D /* MGLTileSource.mm in Sources */,
355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
@@ -2273,6 +2347,7 @@
35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
7E016D861D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */,
DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */,
+ 966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */,
353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
@@ -2301,6 +2376,7 @@
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
DAA4E4221CBB730400178DFB /* MGLPointAnnotation.mm in Sources */,
DAED38661D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */,
+ 1FB7DAB21F2A4DC900410606 /* MGLVectorSource+MGLAdditions.m in Sources */,
DAD165711CF41981001FF4B9 /* MGLFeature.mm in Sources */,
30E5781A1DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */,
40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */,
@@ -2316,7 +2392,9 @@
3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC911D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */,
+ 966FCF551F3C323500F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */,
DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */,
+ 927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */,
350098BE1D480108004B2AF0 /* MGLVectorSource.mm in Sources */,
3566C76F1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */,
DAA4E4351CBB730400178DFB /* SMCalloutView.m in Sources */,
@@ -2356,6 +2434,7 @@
35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */,
DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */,
+ 966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */,
353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
@@ -2464,6 +2543,7 @@
DA618B1E1E688A3700CB7F44 /* ca */,
DA618B2C1E68933600CB7F44 /* fi */,
DAE8CCAE1E6E8C76009B5CB0 /* nl */,
+ DACCD9C81F1F473700BB09A1 /* hu */,
);
name = Root.strings;
sourceTree = "<group>";
@@ -2503,6 +2583,8 @@
DA618B1C1E6888EC00CB7F44 /* ca */,
DA618B251E68920500CB7F44 /* lt */,
DAE9E0F11EB7BF1B001E8E8B /* es */,
+ DA704CBB1F637311004B3F28 /* ru */,
+ DA704CC71F6663A3004B3F28 /* uk */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -2521,6 +2603,7 @@
DA1AC0201E5B8917006DF1D6 /* uk */,
DA618B1D1E6888F500CB7F44 /* ca */,
DA618B261E68920D00CB7F44 /* lt */,
+ DACFE7981F66EA2100630DA8 /* vi */,
);
name = Foundation.stringsdict;
sourceTree = "<group>";
@@ -2546,6 +2629,8 @@
DA57D4AA1EBA8ED300793288 /* es */,
DA57D4AB1EBA909900793288 /* lt */,
DA57D4AC1EBA922A00793288 /* vi */,
+ DA704CBC1F637405004B3F28 /* uk */,
+ DA704CBD1F63746E004B3F28 /* zh-Hant */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
@@ -2758,7 +2843,10 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
+ );
INFOPLIST_FILE = framework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -2773,7 +2861,10 @@
"$(geometry_cflags)",
"$(geojson_cflags)",
);
- OTHER_LDFLAGS = "$(mbgl_core_LINK_LIBRARIES)";
+ OTHER_LDFLAGS = (
+ "$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
+ );
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
PRODUCT_NAME = Mapbox;
SKIP_INSTALL = YES;
@@ -2793,7 +2884,10 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
+ );
INFOPLIST_FILE = framework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -2808,7 +2902,10 @@
"$(geometry_cflags)",
"$(geojson_cflags)",
);
- OTHER_LDFLAGS = "$(mbgl_core_LINK_LIBRARIES)";
+ OTHER_LDFLAGS = (
+ "$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
+ );
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
PRODUCT_NAME = Mapbox;
SKIP_INSTALL = YES;
@@ -2845,7 +2942,10 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
+ );
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
@@ -2859,6 +2959,7 @@
OTHER_LDFLAGS = (
"-ObjC",
"$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_NAME = Mapbox;
PUBLIC_HEADERS_FOLDER_PATH = Headers;
@@ -2872,7 +2973,10 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
+ );
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
@@ -2886,6 +2990,7 @@
OTHER_LDFLAGS = (
"-ObjC",
"$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_NAME = Mapbox;
PUBLIC_HEADERS_FOLDER_PATH = Headers;
diff --git a/platform/ios/resources/es.lproj/Localizable.strings b/platform/ios/resources/es.lproj/Localizable.strings
index 6fbfb23dda..75f799fcd9 100644
--- a/platform/ios/resources/es.lproj/Localizable.strings
+++ b/platform/ios/resources/es.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Muestra más información";
/* No comment provided by engineer. */
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Cancelar";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Regresa al mapa";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Gira el mapa para hacer frente al norte";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Acerca de este mapa";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "No se pudo cargar el mapa debido a un error desconocido.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a un error de carga en el estilo.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -38,11 +47,20 @@
"MAP_A11Y_LABEL" = "Mapa";
/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Zoom %1$dx\nAnotaciones visibles: %2$ld";
+"MAP_A11Y_VALUE" = "Zoom x%1$d\nAnotaciones visibles: %2$ld";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a que el estilo está dañado.";
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "La versión %@ de Mapbox iOS SDK está disponible:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "No se pudo cargar el mapa debido a que no se encuentra el estilo o está incompleto.";
+
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Ayudas a mejorar los mapas de OpenStreetMap y Mapbox al aportar datos de uso anónimos.";
diff --git a/platform/ios/resources/fr.lproj/Localizable.strings b/platform/ios/resources/fr.lproj/Localizable.strings
index 075042c695..7fc85568c7 100644
--- a/platform/ios/resources/fr.lproj/Localizable.strings
+++ b/platform/ios/resources/fr.lproj/Localizable.strings
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Annuler";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Retour à la carte";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Tourne la carte vers le nord";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "À propos de cette carte";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "La carte n’a pas pu être chargée car une erreur inconnue est survenue.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style ne peut pas être chargé.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -40,9 +49,18 @@
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible(s)";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style est corrompu.";
+
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "La version %@ du SDK Mapbox pour iOS est maintenant disponible :";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "La carte n’a pas pu être chargée car le style n’a pas été trouvé ou est incompatible.";
+
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Vous pouvez contribuer à OpenStreetMap et Mapbox en partageant des données d’utilisation anonymes.";
diff --git a/platform/ios/resources/lt.lproj/Localizable.stringsdict b/platform/ios/resources/lt.lproj/Localizable.stringsdict
index 0200327f04..732a8d23ac 100644
--- a/platform/ios/resources/lt.lproj/Localizable.stringsdict
+++ b/platform/ios/resources/lt.lproj/Localizable.stringsdict
@@ -14,11 +14,11 @@
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
- <string>Priartinimas: %dx</string>
+ <string>Priartinimas %dx</string>
<key>few</key>
- <string>Priartinimas: %dx</string>
+ <string>Priartinimas %dx</string>
<key>other</key>
- <string>Priartinimas: %dx</string>
+ <string>Priartinimas %dx</string>
</dict>
<key>count</key>
<dict>
@@ -27,11 +27,11 @@
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
- <string>Matomos anotacijos: %d anotacija</string>
+ <string>%d matoma anotacija</string>
<key>few</key>
- <string>Matomos anotacijos: %d anotacijos</string>
+ <string>%d matomos anotacijos</string>
<key>other</key>
- <string>Matomos anotacijos: %d anotacijų</string>
+ <string>%d matomų anotacijų</string>
</dict>
</dict>
</dict>
diff --git a/platform/ios/resources/ru.lproj/Localizable.strings b/platform/ios/resources/ru.lproj/Localizable.strings
index 1c3b46f057..40f59a3356 100644
--- a/platform/ios/resources/ru.lproj/Localizable.strings
+++ b/platform/ios/resources/ru.lproj/Localizable.strings
@@ -2,7 +2,7 @@
"ANNOTATION_A11Y_HINT" = "Показать больше информации";
/* No comment provided by engineer. */
-"API_CLIENT_400_DESC" = "The session data task failed. Original request was: %@";
+"API_CLIENT_400_DESC" = "The session data task failed. Original request was:%@";
/* No comment provided by engineer. */
"API_CLIENT_400_REASON" = "The status code was %ld";
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Отмена";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Вернуться на карту";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Повернуть карту на север";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Об этой карте";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Не удалось загрузить карту из-за неизвестной ошибки.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту так как невозможно загрузить стиль.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -40,9 +49,18 @@
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Масштаб %1$dx\n%2$ld аннотации(й) видны";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки в стиле.";
+
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK версии %@теперь доступен.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Не удалось загрузить карту так как стиль не найден или несовместим.";
+
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Вы можете помочь сделать карты OpenStreetMap и Mapbox лучше путем предоставления анонимных данных об использовании.";
diff --git a/platform/ios/resources/sv.lproj/Localizable.strings b/platform/ios/resources/sv.lproj/Localizable.strings
index fb787b973a..b10b1b06ed 100644
--- a/platform/ios/resources/sv.lproj/Localizable.strings
+++ b/platform/ios/resources/sv.lproj/Localizable.strings
@@ -38,7 +38,7 @@
"LOAD_MAP_FAILED_DESC" = "Kartan kunde inte laddas på grund av att ett okänt fel inträffade.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "The map failed to load because the style can't be loaded.";
+"LOAD_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen är skadad.";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -50,7 +50,7 @@
"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotering(ar) synliga";
/* User-friendly error description */
-"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted.";
+"PARSE_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen är skadad.";
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
@@ -59,7 +59,7 @@
"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK version %@ är nu tillgängligt:";
/* User-friendly error description */
-"STYLE_NOT_FOUND_DESC" = "The map failed to load because the style can’t be found or is incompatible.";
+"STYLE_NOT_FOUND_DESC" = "Kartan kunde inte laddas på grund av ett okänt fel.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Du kan hjälpa till att göra OpenStreetMap och Mapbox karttjänster bättre genom att bidra med anonymiserad användningsdata.";
diff --git a/platform/ios/resources/uk.lproj/Localizable.strings b/platform/ios/resources/uk.lproj/Localizable.strings
index 86873c69c0..8392f945b8 100644
--- a/platform/ios/resources/uk.lproj/Localizable.strings
+++ b/platform/ios/resources/uk.lproj/Localizable.strings
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Скасувати";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Повернутись до мапи";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Обертає напрямок мапи на північ";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Про мапу";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Неможливо завантажити мапу через невідому помилку.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, бо неможливо завантажити стиль.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -38,13 +47,22 @@
"MAP_A11Y_LABEL" = "Мапа";
/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible";
+"MAP_A11Y_VALUE" = "Масштаб %1$dx\n%2$ld підпис(ів) показано";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, через помилки в стилі.";
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "Доступна версія %@ Mapbox iOS SDK:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Неможливо завантажити мапу, бо неможливо знайти стиль або він несумісний.";
+
/* Telemetry prompt message */
-"TELEMETRY_DISABLED_MSG" = "Ви можете допомогти зробити мапи OpenStreetMap та Mapbox кращими надаючі анонімізовані дані про користування застосунком.";
+"TELEMETRY_DISABLED_MSG" = "Ви можете допомогти зробити мапи OpenStreetMap та Mapbox кращими надаючи анонімізовані дані про користування застосунком.";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_OFF" = "Відмовитись";
@@ -53,7 +71,7 @@
"TELEMETRY_DISABLED_ON" = "Брати участь";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "Ви допомагаєте робити мапи OpenStreetMap та Mapbox кращими надаючі анонімізовані дані про користування застосунком.";
+"TELEMETRY_ENABLED_MSG" = "Ви допомагаєте робити мапи OpenStreetMap та Mapbox кращими надаючи анонімізовані дані про користування застосунком.";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "Припинити участь";
@@ -68,7 +86,7 @@
"TELEMETRY_NAME" = "Телеметрія Mapbox";
/* Telemetry prompt title */
-"TELEMETRY_TITLE" = "Робить мапи Mapbox кращими";
+"TELEMETRY_TITLE" = "Робіть мапи Mapbox кращими";
/* Default user location annotation title */
"USER_DOT_TITLE" = "Ви тут";
diff --git a/platform/ios/resources/uk.lproj/Localizable.stringsdict b/platform/ios/resources/uk.lproj/Localizable.stringsdict
new file mode 100644
index 0000000000..a81b01477e
--- /dev/null
+++ b/platform/ios/resources/uk.lproj/Localizable.stringsdict
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>MAP_A11Y_VALUE</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@level@
+%#@count@</string>
+ <key>level</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>Масштаб %dx</string>
+ <key>few</key>
+ <string>Масштаб %dx</string>
+ <key>other</key>
+ <string>Масштаб %dx</string>
+ </dict>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>Показано %d підпис</string>
+ <key>few</key>
+ <string>Показано %d підписи</string>
+ <key>other</key>
+ <string>Показано %d підпис(ів)</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/ios/resources/zh-Hant.lproj/Localizable.strings b/platform/ios/resources/zh-Hant.lproj/Localizable.strings
index 1b4e7416d9..585ff3dd27 100644
--- a/platform/ios/resources/zh-Hant.lproj/Localizable.strings
+++ b/platform/ios/resources/zh-Hant.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "顯示信息";
/* No comment provided by engineer. */
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "取消";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "回到地圖";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "旋轉地圖使正北朝上";
@@ -19,24 +22,24 @@
/* Compass abbreviation for north */
"COMPASS_NORTH" = "北";
-/* Copyright notice in attribution sheet */
-"COPY_MAPBOX" = "© Mapbox";
-
-/* Copyright notice in attribution sheet */
-"COPY_OSM" = "© OpenStreetMap";
-
/* Instructions in Interface Builder designable; {key}, {plist file name} */
-"DESIGNABLE" = "在%2$@中將你的access token設爲%1$@可在這裏顯示Mapbox上的地圖\n\n更多說明請見";
+"DESIGNABLE" = "在%2$@中將你的access token設爲%1$@即可顯示Mapbox上的地圖\n\n更多說明請見";
/* Setup documentation URL display string; keep as short as possible */
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
/* Accessibility hint */
-"INFO_A11Y_HINT" = "顯示致謝、用戶反饋及更多";
+"INFO_A11Y_HINT" = "顯示致謝、用戶意見表及更多";
/* Accessibility label */
"INFO_A11Y_LABEL" = "關於這個地圖";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "發生不知名的錯誤,無法載入地圖。";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "載入樣式表時發生錯誤,無法載入地圖。";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -44,14 +47,20 @@
"MAP_A11Y_LABEL" = "地圖";
/* Map accessibility value */
-"MAP_A11Y_VALUE" = "地圖縮放%1$d倍\n有%2$ld處標記可見";
+"MAP_A11Y_VALUE" = "縮放地圖%1$d倍\n可顯示%2$ld處標記";
-/* Action in attribution sheet */
-"MAP_FEEDBACK" = "改進地圖";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "樣式表有毀損,無法載入地圖。";
/* Action sheet title */
"SDK_NAME" = "Mapbox iOS SDK";
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK %@版現已開放下載:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "找不到樣式表或樣式表不兼容,無法載入地圖。";
+
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "你可以提供匿名數據來幫助OpenStreetMap和Mapbox的地圖變得更好。";
@@ -62,7 +71,7 @@
"TELEMETRY_DISABLED_ON" = "我要參與";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "你的匿名數據在幫助OpenStreetMap和Mapbox的地圖變得更好。";
+"TELEMETRY_ENABLED_MSG" = "你的匿名數據正在改善OpenStreetMap和Mapbox地圖。";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "不再參與";
@@ -74,11 +83,11 @@
"TELEMETRY_MORE" = "詳細信息";
/* Action in attribution sheet */
-"TELEMETRY_NAME" = "Mapbox傳感數據";
+"TELEMETRY_NAME" = "Mapbox遙測";
/* Telemetry prompt title */
"TELEMETRY_TITLE" = "讓Mapbox地圖變得更好";
/* Default user location annotation title */
-"USER_DOT_TITLE" = "你在這裏";
+"USER_DOT_TITLE" = "你在這裡";
diff --git a/platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict b/platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict
new file mode 100644
index 0000000000..fc44e2e501
--- /dev/null
+++ b/platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>MAP_A11Y_VALUE</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@level@
+%#@count@</string>
+ <key>level</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>other</key>
+ <string>縮放層級%dx</string>
+ </dict>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>other</key>
+ <string>可見%d處標示</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh
index e4403c4652..3a7342034a 100755
--- a/platform/ios/scripts/package.sh
+++ b/platform/ios/scripts/package.sh
@@ -21,33 +21,19 @@ elif [[ ${FORMAT} == "dynamic" ]]; then
BUILD_STATIC=false
fi
-SELF_CONTAINED=${SELF_CONTAINED:-}
-STATIC_BUNDLE_DIR=
-if [[ ${SELF_CONTAINED} ]]; then
- STATIC_BUNDLE_DIR="${OUTPUT}/static/${NAME}.framework"
-else
- STATIC_BUNDLE_DIR="${OUTPUT}/static"
-fi
-
-STATIC_SETTINGS_DIR=
-if [[ ${SELF_CONTAINED} ]]; then
- STATIC_SETTINGS_DIR="${OUTPUT}/static/${NAME}.framework"
-else
- STATIC_SETTINGS_DIR="${OUTPUT}"
-fi
-
SDK=iphonesimulator
if [[ ${BUILD_FOR_DEVICE} == true ]]; then
SDK=iphoneos
fi
IOS_SDK_VERSION=`xcrun --sdk ${SDK} --show-sdk-version`
-echo "Configuring ${FORMAT:-dynamic and static} ${BUILDTYPE} framework for ${SDK}; symbols: ${SYMBOLS}; self-contained static framework: ${SELF_CONTAINED:-NO}"
-
function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; }
function finish { >&2 echo -en "\033[0m"; }
trap finish EXIT
+step "Configuring ${FORMAT:-dynamic and static} ${BUILDTYPE} framework for ${SDK} ${IOS_SDK_VERSION}; symbols: ${SYMBOLS}"
+
+xcodebuild -version
rm -rf ${OUTPUT}
if [[ ${BUILD_STATIC} == true ]]; then
@@ -69,7 +55,7 @@ PROJ_VERSION=$(git rev-list --count HEAD)
SEM_VERSION=$( git describe --tags --match=ios-v*.*.* --abbrev=0 | sed 's/^ios-v//' )
SHORT_VERSION=${SEM_VERSION%-*}
-step "Building targets (build ${PROJ_VERSION}, version ${SEM_VERSION})…"
+step "Building targets (build ${PROJ_VERSION}, version ${SEM_VERSION})"
SCHEME='dynamic'
if [[ ${BUILD_DYNAMIC} == true && ${BUILD_STATIC} == true ]]; then
@@ -78,6 +64,7 @@ elif [[ ${BUILD_STATIC} == true ]]; then
SCHEME='static'
fi
+step "Building for iOS Simulator using scheme ${SCHEME}"
xcodebuild \
CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
CURRENT_SHORT_VERSION=${SHORT_VERSION} \
@@ -92,6 +79,7 @@ xcodebuild \
-jobs ${JOBS} | xcpretty
if [[ ${BUILD_FOR_DEVICE} == true ]]; then
+ step "Building for iOS devices using scheme ${SCHEME}"
xcodebuild \
CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
CURRENT_SHORT_VERSION=${SHORT_VERSION} \
@@ -119,7 +107,7 @@ if [[ ${BUILD_FOR_DEVICE} == true ]]; then
${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
`cmake -LA -N ${DERIVED_DATA} | grep MASON_PACKAGE_icu_LIBRARIES | cut -d= -f2`
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.bundle ${STATIC_BUNDLE_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.bundle ${OUTPUT}/static
fi
if [[ ${BUILD_DYNAMIC} == true ]]; then
@@ -149,7 +137,7 @@ if [[ ${BUILD_FOR_DEVICE} == true ]]; then
-create -output ${OUTPUT}/dynamic/${NAME}.framework/${NAME} | echo
fi
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/Settings.bundle ${STATIC_SETTINGS_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/Settings.bundle ${OUTPUT}
else
if [[ ${BUILD_STATIC} == true ]]; then
step "Assembling static library for iOS Simulator…"
@@ -159,7 +147,7 @@ else
${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
`cmake -LA -N ${DERIVED_DATA} | grep MASON_PACKAGE_icu_LIBRARIES | cut -d= -f2`
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.bundle ${STATIC_BUNDLE_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.bundle ${OUTPUT}/static
fi
if [[ ${BUILD_DYNAMIC} == true ]]; then
@@ -174,7 +162,7 @@ else
fi
fi
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/Settings.bundle ${STATIC_SETTINGS_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/Settings.bundle ${OUTPUT}
fi
if [[ ${SYMBOLS} = NO ]]; then
@@ -244,13 +232,17 @@ if [[ ${BUILD_STATIC} == true ]]; then
fi
step "Copying library resources…"
-cp -pv LICENSE.md ${STATIC_SETTINGS_DIR}
+cp -pv LICENSE.md ${OUTPUT}
if [[ ${BUILD_STATIC} == true ]]; then
- cp -pv "${STATIC_BUNDLE_DIR}/${NAME}.bundle/Info.plist" "${OUTPUT}/static/${NAME}.framework/Info.plist"
+ cp -pv "${OUTPUT}/static/${NAME}.bundle/Info.plist" "${OUTPUT}/static/${NAME}.framework/Info.plist"
plutil -replace CFBundlePackageType -string FMWK "${OUTPUT}/static/${NAME}.framework/Info.plist"
mkdir "${OUTPUT}/static/${NAME}.framework/Modules"
cp -pv platform/ios/framework/modulemap "${OUTPUT}/static/${NAME}.framework/Modules/module.modulemap"
fi
+if [[ ${BUILD_DYNAMIC} == true && ${BUILD_FOR_DEVICE} == true ]]; then
+ step "Copying bitcode symbol maps…"
+ find "${PRODUCTS}/${BUILDTYPE}-iphoneos" -name '*.bcsymbolmap' -type f -exec cp -pv {} "${OUTPUT}/dynamic/" \;
+fi
sed -n -e '/^## /,$p' platform/ios/CHANGELOG.md > "${OUTPUT}/CHANGELOG.md"
rm -rf /tmp/mbgl
diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h
index c48dd6b27b..35fb31a342 100644
--- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h
+++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h
@@ -1,7 +1,15 @@
#import <UIKit/UIKit.h>
#import "MGLUserLocationAnnotationView.h"
+extern const CGFloat MGLUserLocationAnnotationDotSize;
+extern const CGFloat MGLUserLocationAnnotationHaloSize;
+
+extern const CGFloat MGLUserLocationAnnotationPuckSize;
+extern const CGFloat MGLUserLocationAnnotationArrowSize;
+
+// Threshold in radians between heading indicator rotation updates.
+extern const CGFloat MGLUserLocationHeadingUpdateThreshold;
+
@interface MGLFaux3DUserLocationAnnotationView : MGLUserLocationAnnotationView
@end
-
diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m
index 6db9c0db10..21f6aaa540 100644
--- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m
+++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m
@@ -2,6 +2,9 @@
#import "MGLMapView.h"
#import "MGLUserLocation.h"
+#import "MGLUserLocationHeadingIndicator.h"
+#import "MGLUserLocationHeadingArrowLayer.h"
+#import "MGLUserLocationHeadingBeamLayer.h"
const CGFloat MGLUserLocationAnnotationDotSize = 22.0;
const CGFloat MGLUserLocationAnnotationHaloSize = 115.0;
@@ -9,7 +12,7 @@ const CGFloat MGLUserLocationAnnotationHaloSize = 115.0;
const CGFloat MGLUserLocationAnnotationPuckSize = 45.0;
const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6;
-#pragma mark -
+const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
@implementation MGLFaux3DUserLocationAnnotationView
{
@@ -18,14 +21,13 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
CALayer *_puckDot;
CAShapeLayer *_puckArrow;
- CALayer *_headingIndicatorLayer;
- CAShapeLayer *_headingIndicatorMaskLayer;
+ CALayer<MGLUserLocationHeadingIndicator> *_headingIndicatorLayer;
CALayer *_accuracyRingLayer;
CALayer *_dotBorderLayer;
CALayer *_dotLayer;
CALayer *_haloLayer;
- double _oldHeadingAccuracy;
+ CLLocationDirection _oldHeadingAccuracy;
CLLocationAccuracy _oldHorizontalAccuracy;
double _oldZoom;
double _oldPitch;
@@ -56,21 +58,18 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
- (void)setTintColor:(UIColor *)tintColor
{
+ CGColorRef newTintColor = [tintColor CGColor];
+
if (_puckModeActivated)
{
- _puckArrow.fillColor = [tintColor CGColor];
+ _puckArrow.fillColor = newTintColor;
}
else
{
- if (_accuracyRingLayer)
- {
- _accuracyRingLayer.backgroundColor = [tintColor CGColor];
- }
-
- _haloLayer.backgroundColor = [tintColor CGColor];
- _dotLayer.backgroundColor = [tintColor CGColor];
-
- _headingIndicatorLayer.contents = (__bridge id)[[self headingIndicatorTintedGradientImage] CGImage];
+ _accuracyRingLayer.backgroundColor = newTintColor;
+ _haloLayer.backgroundColor = newTintColor;
+ _dotLayer.backgroundColor = newTintColor;
+ [_headingIndicatorLayer updateTintColor:newTintColor];
}
}
@@ -80,7 +79,7 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
{
// disable implicit animation
[CATransaction begin];
- [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
+ [CATransaction setDisableActions:YES];
CATransform3D t = CATransform3DRotate(CATransform3DIdentity, MGLRadiansFromDegrees(self.mapView.camera.pitch), 1.0, 0, 0);
self.layer.sublayerTransform = t;
@@ -138,7 +137,6 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
self.layer.sublayers = nil;
_headingIndicatorLayer = nil;
- _headingIndicatorMaskLayer = nil;
_accuracyRingLayer = nil;
_haloLayer = nil;
_dotBorderLayer = nil;
@@ -225,70 +223,67 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
[self updateFrameWithSize:MGLUserLocationAnnotationDotSize];
}
- BOOL showHeadingIndicator = self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading;
-
- // update heading indicator
+ // heading indicator (tinted, beam or arrow)
//
+ BOOL headingTrackingModeEnabled = self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading;
+ BOOL showHeadingIndicator = self.mapView.showsUserHeadingIndicator || headingTrackingModeEnabled;
+
if (showHeadingIndicator)
{
_headingIndicatorLayer.hidden = NO;
+ CLLocationDirection headingAccuracy = self.userLocation.heading.headingAccuracy;
- // heading indicator (tinted, semi-circle)
- //
- if ( ! _headingIndicatorLayer && self.userLocation.heading.headingAccuracy)
+ if (([_headingIndicatorLayer isMemberOfClass:[MGLUserLocationHeadingBeamLayer class]] && ! headingTrackingModeEnabled) ||
+ ([_headingIndicatorLayer isMemberOfClass:[MGLUserLocationHeadingArrowLayer class]] && headingTrackingModeEnabled))
{
- CGFloat headingIndicatorSize = MGLUserLocationAnnotationHaloSize;
-
- _headingIndicatorLayer = [CALayer layer];
- _headingIndicatorLayer.bounds = CGRectMake(0, 0, headingIndicatorSize, headingIndicatorSize);
- _headingIndicatorLayer.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0);
- _headingIndicatorLayer.contents = (__bridge id)[[self headingIndicatorTintedGradientImage] CGImage];
- _headingIndicatorLayer.contentsGravity = kCAGravityBottom;
- _headingIndicatorLayer.contentsScale = [UIScreen mainScreen].scale;
- _headingIndicatorLayer.opacity = 0.4;
- _headingIndicatorLayer.shouldRasterize = YES;
- _headingIndicatorLayer.rasterizationScale = [UIScreen mainScreen].scale;
- _headingIndicatorLayer.drawsAsynchronously = YES;
-
- [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer];
+ [_headingIndicatorLayer removeFromSuperlayer];
+ _headingIndicatorLayer = nil;
+ _oldHeadingAccuracy = -1;
}
- // heading indicator accuracy mask (fan-shaped)
- //
- if ( ! _headingIndicatorMaskLayer && self.userLocation.heading.headingAccuracy)
+ if ( ! _headingIndicatorLayer && headingAccuracy)
{
- _headingIndicatorMaskLayer = [CAShapeLayer layer];
- _headingIndicatorMaskLayer.frame = _headingIndicatorLayer.bounds;
- _headingIndicatorMaskLayer.path = [[self headingIndicatorClippingMask] CGPath];
-
- // apply the mask to the halo-radius-sized gradient layer
- _headingIndicatorLayer.mask = _headingIndicatorMaskLayer;
-
- _oldHeadingAccuracy = self.userLocation.heading.headingAccuracy;
-
+ if (headingTrackingModeEnabled)
+ {
+ _headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self];
+ [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer];
+ }
+ else
+ {
+ _headingIndicatorLayer = [[MGLUserLocationHeadingArrowLayer alloc] initWithUserLocationAnnotationView:self];
+ [self.layer addSublayer:_headingIndicatorLayer];
+ _headingIndicatorLayer.zPosition = 1;
+ }
}
- else if (_oldHeadingAccuracy != self.userLocation.heading.headingAccuracy)
- {
- // recalculate the clipping mask based on updated accuracy
- _headingIndicatorMaskLayer.path = [[self headingIndicatorClippingMask] CGPath];
- _oldHeadingAccuracy = self.userLocation.heading.headingAccuracy;
+ if (_oldHeadingAccuracy != headingAccuracy)
+ {
+ [_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy];
+ _oldHeadingAccuracy = headingAccuracy;
}
if (self.userLocation.heading.trueHeading >= 0)
{
- _headingIndicatorLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, -MGLRadiansFromDegrees(self.mapView.direction - self.userLocation.heading.trueHeading));
+ CGFloat rotation = -MGLRadiansFromDegrees(self.mapView.direction - self.userLocation.heading.trueHeading);
+
+ // Don't rotate if the change is imperceptible.
+ if (fabs(rotation) > MGLUserLocationHeadingUpdateThreshold)
+ {
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+
+ _headingIndicatorLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, rotation);
+
+ [CATransaction commit];
+ }
}
}
else
{
[_headingIndicatorLayer removeFromSuperlayer];
- [_headingIndicatorMaskLayer removeFromSuperlayer];
_headingIndicatorLayer = nil;
- _headingIndicatorMaskLayer = nil;
}
-
// update accuracy ring (if zoom or horizontal accuracy have changed)
//
if (_accuracyRingLayer && (_oldZoom != self.mapView.zoomLevel || _oldHorizontalAccuracy != self.userLocation.location.horizontalAccuracy))
@@ -301,10 +296,10 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
_accuracyRingLayer.hidden = NO;
// disable implicit animation of the accuracy ring, unless triggered by a change in accuracy
- id shouldDisableActions = (_oldHorizontalAccuracy == self.userLocation.location.horizontalAccuracy) ? (id)kCFBooleanTrue : (id)kCFBooleanFalse;
+ BOOL shouldDisableActions = _oldHorizontalAccuracy == self.userLocation.location.horizontalAccuracy;
[CATransaction begin];
- [CATransaction setValue:shouldDisableActions forKey:kCATransactionDisableActions];
+ [CATransaction setDisableActions:shouldDisableActions];
_accuracyRingLayer.bounds = CGRectMake(0, 0, accuracyRingSize, accuracyRingSize);
_accuracyRingLayer.cornerRadius = accuracyRingSize / 2;
@@ -460,72 +455,8 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
- (CGFloat)calculateAccuracyRingSize
{
- CGFloat latitudeRadians = MGLRadiansFromDegrees(self.userLocation.coordinate.latitude);
- CGFloat metersPerPoint = [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude];
- CGFloat pixelRadius = self.userLocation.location.horizontalAccuracy / cos(latitudeRadians) / metersPerPoint;
-
- return pixelRadius * 2.0;
-}
-
-- (UIImage *)headingIndicatorTintedGradientImage
-{
- UIImage *image;
-
- CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0;
-
- UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0);
-
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGContextRef context = UIGraphicsGetCurrentContext();
-
- // gradient from the tint color to no-alpha tint color
- CGFloat gradientLocations[] = {0.0, 1.0};
- CGGradientRef gradient = CGGradientCreateWithColors(
- colorSpace,
- (__bridge CFArrayRef)@[
- (id)[self.mapView.tintColor CGColor],
- (id)[[self.mapView.tintColor colorWithAlphaComponent:0] CGColor]],
- gradientLocations);
-
- // draw the gradient from the center point to the edge (full halo radius)
- CGPoint centerPoint = CGPointMake(haloRadius, haloRadius);
- CGContextDrawRadialGradient(context, gradient,
- centerPoint, 0.0,
- centerPoint, haloRadius,
- kNilOptions);
-
- image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- CGGradientRelease(gradient);
- CGColorSpaceRelease(colorSpace);
-
- return image;
-}
-
-- (UIBezierPath *)headingIndicatorClippingMask
-{
- CGFloat accuracy = self.userLocation.heading.headingAccuracy;
-
- // size the mask using accuracy, but keep within a good display range
- CGFloat clippingDegrees = 90 - accuracy;
- clippingDegrees = fmin(clippingDegrees, 70); // most accurate
- clippingDegrees = fmax(clippingDegrees, 10); // least accurate
-
- CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize);
- UIBezierPath *ovalPath = UIBezierPath.bezierPath;
-
- // clip the oval to ± incoming accuracy degrees (converted to radians), from the top
- [ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))
- radius:CGRectGetWidth(ovalRect) / 2.0
- startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees)
- endAngle:MGLRadiansFromDegrees(-clippingDegrees)
- clockwise:YES];
-
- [ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))];
- [ovalPath closePath];
-
- return ovalPath;
+ // diameter in screen points
+ return self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0;
}
@end
diff --git a/platform/ios/src/MGLMapView+IBAdditions.h b/platform/ios/src/MGLMapView+IBAdditions.h
index 3766d044d8..d02c938c57 100644
--- a/platform/ios/src/MGLMapView+IBAdditions.h
+++ b/platform/ios/src/MGLMapView+IBAdditions.h
@@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) IBInspectable BOOL allowsRotating;
@property (nonatomic) IBInspectable BOOL allowsTilting;
@property (nonatomic) IBInspectable BOOL showsUserLocation;
+@property (nonatomic) IBInspectable BOOL showsHeading;
@end
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 7a1d200e44..e2c070a54f 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -253,6 +253,9 @@ MGL_EXPORT IB_DESIGNABLE
A view showing legally required copyright notices and telemetry settings,
positioned at the bottom-right of the map view.
+ If you choose to reimplement this view, assign the `-showAttribution:` method
+ as the action for your view to present the default notices and settings.
+
@note The Mapbox terms of service, which governs the use of Mapbox-hosted
vector tiles and styles,
<a href="https://www.mapbox.com/help/attribution/">requires</a> these
@@ -272,23 +275,24 @@ MGL_EXPORT IB_DESIGNABLE
@property (nonatomic, readonly) UIButton *attributionButton;
/**
- Support for style classes has been removed. This property always returns an empty array.
+ Show the attribution and telemetry action sheet.
+
+ This action is performed when the user taps on the attribution button provided
+ by default via the `attributionButton` property. If you implement a custom
+ attribution button, you should add this action to the button.
*/
+- (IBAction)showAttribution:(id)sender;
+
+/// :nodoc: Support for style classes has been removed. This property always returns an empty array.
@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional.")));
-/**
- Support for style classes has been removed. This property always returns NO.
- */
+/// :nodoc: Support for style classes has been removed. This property always returns NO.
- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
-/**
- Support for style classes has been removed. This property is a no-op.
- */
+/// :nodoc: Support for style classes has been removed. This property is a no-op.
- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
-/**
- Support for style classes has been removed. This property is a no-op.
- */
+/// :nodoc: Support for style classes has been removed. This property is a no-op.
- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
#pragma mark Displaying the User’s Location
@@ -370,6 +374,23 @@ MGL_EXPORT IB_DESIGNABLE
- (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment animated:(BOOL)animated;
/**
+ A Boolean value indicating whether the user location annotation may display a
+ permanent heading indicator.
+
+ Setting this property to `YES` causes the default user location annotation to
+ appear and always show an arrow-shaped heading indicator, if heading is
+ available. This property does not rotate the map; for that, see
+ `MGLUserTrackingModeFollowWithHeading`.
+
+ This property has no effect when `userTrackingMode` is
+ `MGLUserTrackingModeFollowWithHeading` or
+ `MGLUserTrackingModeFollowWithCourse`.
+
+ The default value of this property is `NO`.
+ */
+@property (nonatomic, assign) BOOL showsUserHeadingIndicator;
+
+/**
Whether the map view should display a heading calibration alert when necessary.
The default value is `YES`.
*/
@@ -589,7 +610,7 @@ MGL_EXPORT IB_DESIGNABLE
*
* The default minimumZoomLevel is 0.
*/
-@property (nonatomic) double minimumZoomLevel;
+@property (nonatomic) IBInspectable double minimumZoomLevel;
/**
* The maximum zoom level the map can be shown at.
@@ -597,9 +618,10 @@ MGL_EXPORT IB_DESIGNABLE
* If the value of this property is smaller than that of the
* minimumZoomLevel property, the behavior is undefined.
*
- * The default maximumZoomLevel is 20.
+ * The default maximumZoomLevel is 22. The upper bound for this property
+ * is 25.5.
*/
-@property (nonatomic) double maximumZoomLevel;
+@property (nonatomic) IBInspectable double maximumZoomLevel;
/**
The heading of the map, measured in degrees clockwise from true north.
@@ -777,6 +799,23 @@ MGL_EXPORT IB_DESIGNABLE
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
/**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition duration and timing function.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously.
+ @param function A timing function used for the animation. Set this parameter to
+ `nil` for a transition that matches most system animations. If the duration
+ is `0`, this parameter is ignored.
+ @param edgePadding The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(UIEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion;
+
+/**
Moves the viewpoint to a different location using a transition animation that
evokes powered flight and a default duration based on the length of the flight
path.
@@ -851,6 +890,20 @@ MGL_EXPORT IB_DESIGNABLE
- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets;
/**
+ Returns the camera that best fits the given shape, with the specified direction,
+ optionally with some additional padding on each side.
+
+ @param shape The shape to fit to the receiver’s viewport.
+ @param direction The direction of the viewport, measured in degrees clockwise from true north.
+ @param insets The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @return A camera object centered on the shape's center with zoom level as high
+ (close to the ground) as possible while still including the entire shape. The
+ camera object uses the current pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets;
+
+/**
Returns the point in this view’s coordinate system on which to "anchor" in
response to a user-initiated gesture.
@@ -986,16 +1039,6 @@ MGL_EXPORT IB_DESIGNABLE
@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
/**
- The complete list of annotations associated with the receiver that are
- currently visible.
-
- The objects in this array must adopt the `MGLAnnotation` protocol. If no
- annotations are associated with the map view or if no annotations associated
- with the map view are currently visible, the value of this property is `nil`.
- */
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *visibleAnnotations;
-
-/**
Adds an annotation to the map view.
@note `MGLMultiPolyline`, `MGLMultiPolygon`, `MGLShapeCollection`, and
@@ -1090,6 +1133,16 @@ MGL_EXPORT IB_DESIGNABLE
- (nullable __kindof MGLAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier;
/**
+ The complete list of annotations associated with the receiver that are
+ currently visible.
+
+ The objects in this array must adopt the `MGLAnnotation` protocol. If no
+ annotations are associated with the map view or if no annotations associated
+ with the map view are currently visible, the value of this property is `nil`.
+ */
+@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *visibleAnnotations;
+
+/**
Returns the list of annotations associated with the receiver that intersect with
the given rectangle.
@@ -1258,6 +1311,11 @@ MGL_EXPORT IB_DESIGNABLE
`-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
`-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
+ The returned features may also include features corresponding to annotations.
+ These features are not object-equal to the `MGLAnnotation` objects that were
+ originally added to the map. To query the map for annotations, use
+ `visibleAnnotations` or `-[MGLMapView visibleAnnotationsInRect:]`.
+
@note Layer identifiers are not guaranteed to exist across styles or different
versions of the same style. Applications that use this API must first set
the style URL to an explicitly versioned style using a convenience method
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 175e1f125d..0e76c0c71c 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -35,6 +35,7 @@
#include <mbgl/util/projection.hpp>
#import "Mapbox.h"
+#import "MGLShape_Private.h"
#import "MGLFeature_Private.h"
#import "MGLGeometry_Private.h"
#import "MGLMultiPoint_Private.h"
@@ -138,9 +139,6 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1;
const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10);
-// Context for KVO observing UILayoutGuides.
-static void * MGLLayoutGuidesUpdatedContext = &MGLLayoutGuidesUpdatedContext;
-
/// Unique identifier representing a single annotation in mbgl.
typedef uint32_t MGLAnnotationTag;
@@ -242,10 +240,14 @@ public:
@property (nonatomic) EAGLContext *context;
@property (nonatomic) GLKView *glView;
@property (nonatomic) UIImageView *glSnapshotView;
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints;
@property (nonatomic, readwrite) MGLScaleBar *scaleBar;
@property (nonatomic, readwrite) UIImageView *compassView;
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints;
@property (nonatomic, readwrite) UIImageView *logoView;
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints;
@property (nonatomic, readwrite) UIButton *attributionButton;
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints;
@property (nonatomic, readwrite) MGLStyle *style;
@property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer;
@property (nonatomic) UITapGestureRecognizer *doubleTap;
@@ -302,8 +304,6 @@ public:
NSDate *_userLocationAnimationCompletionDate;
/// True if a willChange notification has been issued for shape annotation layers and a didChange notification is pending.
BOOL _isChangingAnnotationLayers;
- BOOL _isObservingTopLayoutGuide;
- BOOL _isObservingBottomLayoutGuide;
BOOL _isWaitingForRedundantReachableNotification;
BOOL _isTargetingInterfaceBuilder;
@@ -483,21 +483,31 @@ public:
_selectedAnnotationTag = MGLAnnotationTagNotFound;
_annotationsNearbyLastTap = {};
- // setup logo bug
+ // setup logo
//
UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox"];
_logoView = [[UIImageView alloc] initWithImage:logo];
_logoView.accessibilityTraits = UIAccessibilityTraitStaticText;
_logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label");
+ _logoView.translatesAutoresizingMaskIntoConstraints = NO;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+ if ([_logoView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _logoView.accessibilityIgnoresInvertColors = YES; }
+#endif
[self addSubview:_logoView];
+ _logoViewConstraints = [NSMutableArray array];
// setup attribution
//
_attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
_attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label");
_attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint");
- [_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside];
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+ if ([_attributionButton respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _attributionButton.accessibilityIgnoresInvertColors = YES; }
+#endif
+ [_attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchUpInside];
+ _attributionButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_attributionButton];
+ _attributionButtonConstraints = [NSMutableArray array];
[_attributionButton addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:NULL];
// setup compass
@@ -509,12 +519,22 @@ public:
_compassView.accessibilityTraits = UIAccessibilityTraitButton;
_compassView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label");
_compassView.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint");
+ _compassView.translatesAutoresizingMaskIntoConstraints = NO;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+ if ([_compassView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _compassView.accessibilityIgnoresInvertColors = YES; }
+#endif
[self addSubview:_compassView];
+ _compassViewConstraints = [NSMutableArray array];
// setup scale control
//
_scaleBar = [[MGLScaleBar alloc] init];
+ _scaleBar.translatesAutoresizingMaskIntoConstraints = NO;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+ if ([_scaleBar respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _scaleBar.accessibilityIgnoresInvertColors = YES; }
+#endif
[self addSubview:_scaleBar];
+ _scaleBarConstraints = [NSMutableArray array];
// setup interaction
//
@@ -627,6 +647,9 @@ public:
_glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
_glView.layer.opaque = _opaque;
_glView.delegate = self;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+ if ([_glView respondsToSelector:@selector(accessibilityIgnoresInvertColors)]) { _glView.accessibilityIgnoresInvertColors = YES; }
+#endif
[_glView bindDrawable];
[self insertSubview:_glView atIndex:0];
_glView.contentMode = UIViewContentModeCenter;
@@ -674,48 +697,6 @@ public:
_isWaitingForRedundantReachableNotification = NO;
}
-- (void)willMoveToWindow:(UIWindow *)newWindow
-{
- [super willMoveToWindow:newWindow];
-
- if (newWindow) {
- [self addLayoutGuideObserversIfNeeded];
- } else {
- [self removeLayoutGuideObserversIfNeeded];
- }
-}
-
-- (void)addLayoutGuideObserversIfNeeded
-{
- UIViewController *viewController = self.viewControllerForLayoutGuides;
- BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
-
- if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) {
- [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingTopLayoutGuide = YES;
- }
-
- if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) {
- [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingBottomLayoutGuide = YES;
- }
-}
-
-- (void)removeLayoutGuideObserversIfNeeded
-{
- UIViewController *viewController = self.viewControllerForLayoutGuides;
-
- if (_isObservingTopLayoutGuide) {
- [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingTopLayoutGuide = NO;
- }
-
- if (_isObservingBottomLayoutGuide) {
- [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingBottomLayoutGuide = NO;
- }
-}
-
- (void)dealloc
{
[_reachability stopNotifier];
@@ -724,8 +705,6 @@ public:
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_attributionButton removeObserver:self forKeyPath:@"hidden"];
- [self removeLayoutGuideObserversIfNeeded];
-
// Removing the annotations unregisters any outstanding KVO observers.
NSArray *annotations = self.annotations;
if (annotations)
@@ -751,6 +730,18 @@ public:
{
[EAGLContext setCurrentContext:nil];
}
+
+ [self.compassViewConstraints removeAllObjects];
+ self.compassViewConstraints = nil;
+
+ [self.scaleBarConstraints removeAllObjects];
+ self.scaleBarConstraints = nil;
+
+ [self.logoViewConstraints removeAllObjects];
+ self.logoViewConstraints = nil;
+
+ [self.attributionButtonConstraints removeAllObjects];
+ self.attributionButtonConstraints = nil;
}
- (void)setDelegate:(nullable id<MGLMapViewDelegate>)delegate
@@ -797,15 +788,207 @@ public:
return nil;
}
+- (void)updateConstraintsPreiOS11 {
+ // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets
+ // is set to YES, use its view as the parent for constraints. -[MGLMapView adjustContentInset]
+ // already take top and bottom layout guides into account. If we don't have a reference, apply
+ // constraints against ourself to maintain placement of the subviews.
+ //
+ UIViewController *viewController = self.viewControllerForLayoutGuides;
+ BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
+ UIView *containerView = useLayoutGuides ? viewController.view : self;
+
+ // compass view
+ //
+ [containerView removeConstraints:self.compassViewConstraints];
+ [self.compassViewConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.compassViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.compassView
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:viewController.topLayoutGuide
+ attribute:NSLayoutAttributeBottom
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ }
+ [self.compassViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.compassView
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTop
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ [self.compassViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.compassView
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1.0
+ constant:5.0 + self.contentInset.right]];
+
+ [containerView addConstraints:self.compassViewConstraints];
+
+ // scale bar view
+ //
+ [containerView removeConstraints:self.scaleBarConstraints];
+ [self.scaleBarConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.scaleBarConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.scaleBar
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:viewController.topLayoutGuide
+ attribute:NSLayoutAttributeBottom
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ }
+ [self.scaleBarConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.scaleBar
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTop
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ [self.scaleBarConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.scaleBar
+ attribute:NSLayoutAttributeLeft
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeLeft
+ multiplier:1.0
+ constant:8.0 + self.contentInset.left]];
+
+ [containerView addConstraints:self.scaleBarConstraints];
+
+ // logo view
+ //
+ [containerView removeConstraints:self.logoViewConstraints];
+ [self.logoViewConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.logoViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.logoView
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1.0
+ constant:8.0 + self.contentInset.bottom]];
+ }
+ [self.logoViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.logoView
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8 + self.contentInset.bottom]];
+ [self.logoViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.logoView
+ attribute:NSLayoutAttributeLeading
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeLeading
+ multiplier:1.0
+ constant:8.0 + self.contentInset.left]];
+ [containerView addConstraints:self.logoViewConstraints];
+
+ // attribution button
+ //
+ [containerView removeConstraints:self.attributionButtonConstraints];
+ [self.attributionButtonConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.attributionButtonConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8 + self.contentInset.bottom]];
+ }
+ [self.attributionButtonConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8 + self.contentInset.bottom]];
+
+ [self.attributionButtonConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1
+ constant:8 + self.contentInset.right]];
+ [containerView addConstraints:self.attributionButtonConstraints];
+}
+
- (void)updateConstraints
{
- [super updateConstraints];
- // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets
- // is set to YES, -[MGLMapView adjustContentInset] takes top and bottom layout
- // guides into account. To get notified about changes to the layout guides,
- // we need to observe their bounds and re-layout accordingly.
- [self addLayoutGuideObserversIfNeeded];
+// If compiling with the iOS 11+ SDK
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+ // If safeAreaLayoutGuide API exists
+ if ( [self respondsToSelector:@selector(safeAreaLayoutGuide)] ) {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+ UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide;
+#pragma clang diagnostic pop
+ // compass view
+ [self removeConstraints:self.compassViewConstraints];
+ [self.compassViewConstraints removeAllObjects];
+ [self.compassViewConstraints addObject:[self.compassView.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor
+ constant:5.0 + self.contentInset.top]];
+ [self.compassViewConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:self.compassView.rightAnchor
+ constant:8.0 + self.contentInset.right]];
+ [self addConstraints:self.compassViewConstraints];
+
+ // scale bar view
+ [self removeConstraints:self.scaleBarConstraints];
+ [self.scaleBarConstraints removeAllObjects];
+ [self.scaleBarConstraints addObject:[self.scaleBar.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor
+ constant:5.0 + self.contentInset.top]];
+ [self.scaleBarConstraints addObject:[self.scaleBar.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor
+ constant:8.0 + self.contentInset.left]];
+ [self addConstraints:self.scaleBarConstraints];
+
+ // logo view
+ [self removeConstraints:self.logoViewConstraints];
+ [self.logoViewConstraints removeAllObjects];
+ [self.logoViewConstraints addObject:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:self.logoView.bottomAnchor
+ constant:8.0 + self.contentInset.bottom]];
+ [self.logoViewConstraints addObject:[self.logoView.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor
+ constant:8.0 + self.contentInset.left]];
+ [self addConstraints:self.logoViewConstraints];
+
+ // attribution button
+ [self removeConstraints:self.attributionButtonConstraints];
+ [self.attributionButtonConstraints removeAllObjects];
+ [self.attributionButtonConstraints addObject:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:self.attributionButton.bottomAnchor
+ constant:8.0 + self.contentInset.bottom]];
+ [self.attributionButtonConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:self.attributionButton.rightAnchor
+ constant:8.0 + self.contentInset.right]];
+ [self addConstraints:self.attributionButtonConstraints];
+ } else {
+ [self updateConstraintsPreiOS11];
+ }
+#else
+ [self updateConstraintsPreiOS11];
+#endif
+
+ [super updateConstraints];
}
- (BOOL)isOpaque
@@ -835,8 +1018,6 @@ public:
[super layoutSubviews];
[self adjustContentInset];
-
- [self layoutOrnaments];
if (!_isTargetingInterfaceBuilder) {
_mbglMap->setSize([self size]);
@@ -844,44 +1025,15 @@ public:
if (self.compassView.alpha)
{
- [self updateHeadingForDeviceOrientation];
[self updateCompass];
}
- [self updateUserLocationAnnotationView];
-}
+ if (self.compassView.alpha || self.showsUserHeadingIndicator)
+ {
+ [self updateHeadingForDeviceOrientation];
+ }
-- (void)layoutOrnaments
-{
- // scale bar
- self.scaleBar.frame = {
- self.contentInset.left+8,
- self.contentInset.top+5,
- CGRectGetWidth(self.scaleBar.frame),
- CGRectGetHeight(self.scaleBar.frame)
- };
-
- // compass
- self.compassView.center = {
- .x = CGRectGetWidth(self.bounds)-CGRectGetMidX(self.compassView.bounds)-self.contentInset.right-5,
- .y = CGRectGetMidY(self.compassView.bounds)+self.contentInset.top+5
- };
-
- // logo bug
- self.logoView.frame = {
- self.contentInset.left+8,
- CGRectGetHeight(self.bounds)-8-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds),
- CGRectGetWidth(self.logoView.bounds),
- CGRectGetHeight(self.logoView.bounds)
- };
-
- // attribution
- self.attributionButton.frame = {
- CGRectGetWidth(self.bounds)-CGRectGetWidth(self.attributionButton.bounds)-self.contentInset.right-8,
- CGRectGetHeight(self.bounds)-CGRectGetHeight(self.attributionButton.bounds)-self.contentInset.bottom-8,
- CGRectGetWidth(self.attributionButton.bounds),
- CGRectGetHeight(self.attributionButton.bounds)
- };
+ [self updateUserLocationAnnotationView];
}
/// Updates `contentInset` to reflect the current window geometry.
@@ -1116,8 +1268,10 @@ public:
- (void)updateTintColorForView:(UIView *)view
{
- // stop at recursing container & annotation views (#8522)
- if ([view isEqual:self.annotationContainerView]) return;
+ // Don't update:
+ // - annotation views
+ // - attribution button (handled automatically)
+ if ([view isEqual:self.annotationContainerView] || [view isEqual:self.attributionButton]) return;
if ([view respondsToSelector:@selector(setTintColor:)]) view.tintColor = self.tintColor;
@@ -1250,8 +1404,6 @@ public:
{
if ( ! self.isZoomEnabled) return;
- if (_mbglMap->getZoom() <= _mbglMap->getMinZoom() && pinch.scale < 1) return;
-
_mbglMap->cancelTransitions();
CGPoint centerPoint = [self anchorPointForGesture:pinch];
@@ -1267,17 +1419,17 @@ public:
}
else if (pinch.state == UIGestureRecognizerStateChanged)
{
+ // Zoom limiting happens at the core level.
CGFloat newScale = self.scale * pinch.scale;
- double zoom = log2(newScale);
- if (zoom < _mbglMap->getMinZoom()) return;
-
+ double newZoom = log2(newScale);
+
// Calculates the final camera zoom, has no effect within current map camera.
- MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint];
+ MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint];
if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
[self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- _mbglMap->setZoom(zoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ _mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
// The gesture recognizer only reports the gesture’s current center
// point, so use the previous center point to anchor the transition.
// If the number of touches has changed, the remembered center point is
@@ -1625,9 +1777,9 @@ public:
{
CGFloat distance = [quickZoom locationInView:quickZoom.view].y - self.quickZoomStart;
- CGFloat newZoom = log2f(self.scale) + (distance / 75);
+ CGFloat newZoom = MAX(log2f(self.scale) + (distance / 75), _mbglMap->getMinZoom());
- if (newZoom < _mbglMap->getMinZoom()) return;
+ if (_mbglMap->getZoom() == newZoom) return;
CGPoint centerPoint = [self anchorPointForGesture:quickZoom];
@@ -1760,39 +1912,6 @@ public:
return [gesture locationInView:gesture.view];
}
-- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
-{
- if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
- {
- UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
-
- if (panGesture.minimumNumberOfTouches == 2)
- {
- CGPoint velocity = [panGesture velocityInView:panGesture.view];
- double gestureAngle = MGLDegreesFromRadians(atan(velocity.y / velocity.x));
- double horizontalToleranceDegrees = 20.0;
-
- // cancel if gesture angle is not 90º±20º (more or less vertical)
- if ( ! (fabs((fabs(gestureAngle) - 90.0)) < horizontalToleranceDegrees))
- {
- return NO;
- }
- }
- }
- else if (gestureRecognizer == _singleTapGestureRecognizer)
- {
- // Gesture will be recognized if it could deselect an annotation
- if(!self.selectedAnnotation)
- {
- id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO];
- if(!annotation) {
- return NO;
- }
- }
- }
- return YES;
-}
-
- (void)handleCalloutAccessoryTapGesture:(UITapGestureRecognizer *)tap
{
if ([self.delegate respondsToSelector:@selector(mapView:annotation:calloutAccessoryControlTapped:)])
@@ -1836,12 +1955,60 @@ public:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, calloutView);
}
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
+{
+ if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
+ {
+ UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
+
+ if (panGesture.minimumNumberOfTouches == 2)
+ {
+ CGPoint west = [panGesture locationOfTouch:0 inView:panGesture.view];
+ CGPoint east = [panGesture locationOfTouch:1 inView:panGesture.view];
+
+ if (west.x > east.x) {
+ CGPoint swap = west;
+ west = east;
+ east = swap;
+ }
+
+ CLLocationDegrees horizontalToleranceDegrees = 60.0;
+ if ([self angleBetweenPoints:west east:east] > horizontalToleranceDegrees) {
+ return NO;
+ }
+
+ }
+ }
+ else if (gestureRecognizer == _singleTapGestureRecognizer)
+ {
+ // Gesture will be recognized if it could deselect an annotation
+ if(!self.selectedAnnotation)
+ {
+ id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO];
+ if(!annotation) {
+ return NO;
+ }
+ }
+ }
+ return YES;
+}
+
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSArray *validSimultaneousGestures = @[ self.pan, self.pinch, self.rotate ];
return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]);
}
+
+- (CLLocationDegrees)angleBetweenPoints:(CGPoint)west east:(CGPoint)east
+{
+ CGFloat slope = (west.y - east.y) / (west.x - east.x);
+
+ CGFloat angle = atan(fabs(slope));
+ CLLocationDegrees degrees = MGLDegreesFromRadians(angle);
+
+ return degrees;
+}
- (void)trackGestureEvent:(NSString *)gestureID forRecognizer:(UIGestureRecognizer *)recognizer
{
@@ -1859,7 +2026,7 @@ public:
#pragma mark - Attribution -
-- (void)showAttribution
+- (void)showAttribution:(__unused id)sender
{
NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title");
UIAlertController *attributionController = [UIAlertController alertControllerWithTitle:title
@@ -2028,10 +2195,6 @@ public:
[self updateCalloutView];
}
}
- else if (context == MGLLayoutGuidesUpdatedContext && [keyPath isEqualToString:@"bounds"])
- {
- [self setNeedsLayout];
- }
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled
@@ -2136,10 +2299,11 @@ public:
- (void)resetPosition
{
- CGFloat pitch = _mbglMap->getStyle().getDefaultPitch();
- CLLocationDirection heading = mbgl::util::wrap(_mbglMap->getStyle().getDefaultBearing(), 0., 360.);
- CLLocationDistance distance = MGLAltitudeForZoomLevel(_mbglMap->getStyle().getDefaultZoom(), pitch, 0, self.frame.size);
- self.camera = [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(_mbglMap->getStyle().getDefaultLatLng())
+ auto camera = _mbglMap->getStyle().getDefaultCamera();
+ CGFloat pitch = *camera.pitch;
+ CLLocationDirection heading = mbgl::util::wrap(*camera.angle, 0., 360.);
+ CLLocationDistance distance = MGLAltitudeForZoomLevel(*camera.zoom, pitch, 0, self.frame.size);
+ self.camera = [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(*camera.center)
fromDistance:distance
pitch:pitch
heading:heading];
@@ -2742,6 +2906,10 @@ public:
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion
{
+ [self setCamera:camera withDuration:duration animationTimingFunction:function edgePadding:self.contentInset completionHandler:completion];
+}
+
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(UIEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion {
mbgl::AnimationOptions animationOptions;
if (duration > 0)
{
@@ -2770,7 +2938,7 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
- mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:self.contentInset];
+ mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding];
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
}
@@ -2844,6 +3012,16 @@ public:
return [self cameraForCameraOptions:cameraOptions];
}
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets {
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForGeometry([shape geometryObject], padding, direction);
+
+ return [self cameraForCameraOptions:cameraOptions];
+
+}
+
- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions
{
CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng());
@@ -3776,11 +3954,6 @@ public:
if (annotation == self.selectedAnnotation) return;
- if (annotation != self.userLocation)
- {
- self.userTrackingMode = MGLUserTrackingModeNone;
- }
-
[self deselectAnnotation:self.selectedAnnotation animated:NO];
// Add the annotation to the map if it hasn’t been added yet.
@@ -4126,33 +4299,41 @@ public:
{
self.locationManager = [[CLLocationManager alloc] init];
- if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)] && [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)
+ if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)
{
- BOOL hasLocationDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] || [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"];
- if (!hasLocationDescription)
+ BOOL requiresWhenInUseUsageDescription = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){11,0,0}];
+ BOOL hasWhenInUseUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"];
+ BOOL hasAlwaysUsageDescription;
+ if (requiresWhenInUseUsageDescription)
{
- [NSException raise:@"Missing Location Services usage description" format:
- @"This app must have a value for NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription in its Info.plist."];
+ hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"] && hasWhenInUseUsageDescription;
+ }
+ else
+ {
+ hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"];
}
- if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"])
+ if (hasAlwaysUsageDescription)
{
[self.locationManager requestAlwaysAuthorization];
}
- else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"])
+ else if (hasWhenInUseUsageDescription)
{
[self.locationManager requestWhenInUseAuthorization];
}
+ else
+ {
+ NSString *suggestedUsageKeys = requiresWhenInUseUsageDescription ?
+ @"NSLocationWhenInUseUsageDescription and (optionally) NSLocationAlwaysAndWhenInUseUsageDescription" :
+ @"NSLocationWhenInUseUsageDescription and/or NSLocationAlwaysUsageDescription";
+ [NSException raise:@"Missing Location Services usage description" format:@"This app must have a value for %@ in its Info.plist.", suggestedUsageKeys];
+ }
}
- self.locationManager.headingFilter = 5.0;
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
- if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)
- {
- [self.locationManager startUpdatingHeading];
- }
+ [self validateUserHeadingUpdating];
}
else if ( ! shouldEnableLocationServices && self.locationManager)
{
@@ -4276,8 +4457,6 @@ public:
{
self.userTrackingState = MGLUserTrackingStatePossible;
- [self.locationManager stopUpdatingHeading];
-
// Immediately update the annotation view; other cases update inside
// the locationManager:didUpdateLocations: method.
[self updateUserLocationAnnotationView];
@@ -4290,14 +4469,6 @@ public:
self.userTrackingState = animated ? MGLUserTrackingStatePossible : MGLUserTrackingStateChanged;
self.showsUserLocation = YES;
- [self.locationManager stopUpdatingHeading];
-
- CLLocation *location = self.userLocation.location;
- if (location && self.userLocationAnnotationView)
- {
- [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
- }
-
break;
}
case MGLUserTrackingModeFollowWithHeading:
@@ -4314,19 +4485,21 @@ public:
[self setZoomLevel:self.currentMinimumZoom animated:YES];
}
- if (self.userLocationAnnotationView)
- {
- [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated];
- }
-
- [self updateHeadingForDeviceOrientation];
-
- [self.locationManager startUpdatingHeading];
-
break;
}
}
+ if (_userTrackingMode != MGLUserTrackingModeNone)
+ {
+ CLLocation *location = self.userLocation.location;
+ if (location && self.userLocationAnnotationView)
+ {
+ [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
+ }
+ }
+
+ [self validateUserHeadingUpdating];
+
if ([self.delegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)])
{
[self.delegate mapView:self didChangeUserTrackingMode:_userTrackingMode animated:animated];
@@ -4365,14 +4538,43 @@ public:
if (self.userTrackingMode == MGLUserTrackingModeFollowWithCourse)
{
self.userTrackingState = MGLUserTrackingStatePossible;
- if (self.userLocation.location)
+
+ CLLocation *location = self.userLocation.location;
+ if (location)
{
- [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated];
+ [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
}
}
}
}
+- (void)setShowsUserHeadingIndicator:(BOOL)showsUserHeadingIndicator
+{
+ _showsUserHeadingIndicator = showsUserHeadingIndicator;
+
+ if (_showsUserHeadingIndicator)
+ {
+ self.showsUserLocation = YES;
+
+ }
+ [self validateUserHeadingUpdating];
+}
+
+- (void)validateUserHeadingUpdating
+{
+ BOOL canShowPermanentHeadingIndicator = self.showsUserHeadingIndicator && self.userTrackingMode != MGLUserTrackingModeFollowWithCourse;
+
+ if (canShowPermanentHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)
+ {
+ [self updateHeadingForDeviceOrientation];
+ [self.locationManager startUpdatingHeading];
+ }
+ else
+ {
+ [self.locationManager stopUpdatingHeading];
+ }
+}
+
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
[self locationManager:manager didUpdateLocations:locations animated:YES];
@@ -4620,6 +4822,11 @@ public:
self.userLocation.heading = newHeading;
+ if (self.showsUserHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)
+ {
+ [self updateUserLocationAnnotationView];
+ }
+
if ([self.delegate respondsToSelector:@selector(mapView:didUpdateUserLocation:)])
{
[self.delegate mapView:self didUpdateUserLocation:self.userLocation];
@@ -4656,30 +4863,39 @@ public:
{
// note that right/left device and interface orientations are opposites (see UIApplication.h)
//
+ CLDeviceOrientation orientation;
switch ([[UIApplication sharedApplication] statusBarOrientation])
{
case (UIInterfaceOrientationLandscapeLeft):
{
- self.locationManager.headingOrientation = CLDeviceOrientationLandscapeRight;
+ orientation = CLDeviceOrientationLandscapeRight;
break;
}
case (UIInterfaceOrientationLandscapeRight):
{
- self.locationManager.headingOrientation = CLDeviceOrientationLandscapeLeft;
+ orientation = CLDeviceOrientationLandscapeLeft;
break;
}
case (UIInterfaceOrientationPortraitUpsideDown):
{
- self.locationManager.headingOrientation = CLDeviceOrientationPortraitUpsideDown;
+ orientation = CLDeviceOrientationPortraitUpsideDown;
break;
}
case (UIInterfaceOrientationPortrait):
default:
{
- self.locationManager.headingOrientation = CLDeviceOrientationPortrait;
+ orientation = CLDeviceOrientationPortrait;
break;
}
}
+
+ // Setting the location manager's heading orientation causes it to send
+ // a heading event, which in turn makes us redraw, which kicks off a
+ // loop... so don't do that. rdar://34059173
+ if (self.locationManager.headingOrientation != orientation)
+ {
+ self.locationManager.headingOrientation = orientation;
+ }
}
}
@@ -5444,6 +5660,10 @@ public:
}
}
+ mbgl::Size getFramebufferSize() const override {
+ return nativeView.framebufferSize;
+ }
+
void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override {
bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated;
[nativeView cameraWillChangeAnimated:animated];
@@ -5695,4 +5915,19 @@ private:
self.pitchEnabled = allowsTilting;
}
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingShowsHeading
+{
+ return [NSSet setWithObject:@"showsUserHeadingIndicator"];
+}
+
+- (BOOL)showsHeading
+{
+ return self.showsUserHeadingIndicator;
+}
+
+- (void)setShowsHeading:(BOOL)showsHeading
+{
+ self.showsUserHeadingIndicator = showsHeading;
+}
+
@end
diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h
index 8c6fe46136..91abadbcb7 100644
--- a/platform/ios/src/MGLUserLocation.h
+++ b/platform/ios/src/MGLUserLocation.h
@@ -20,8 +20,7 @@ MGL_EXPORT
/**
The current location of the device. (read-only)
- This property contains `nil` if the map view is not currently showing the user
- location or if the user’s location has not yet been determined.
+ This property returns `nil` if the user’s location has not yet been determined.
*/
@property (nonatomic, readonly, nullable) CLLocation *location;
diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m
index 1c9649c09e..074d138a72 100644
--- a/platform/ios/src/MGLUserLocation.m
+++ b/platform/ios/src/MGLUserLocation.m
@@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_END
{
if (self = [super init])
{
- _location = [[CLLocation alloc] initWithLatitude:MAXFLOAT longitude:MAXFLOAT];
_mapView = mapView;
}
@@ -102,7 +101,7 @@ NS_ASSUME_NONNULL_END
- (CLLocationCoordinate2D)coordinate
{
- return self.location.coordinate;
+ return _location ? _location.coordinate : kCLLocationCoordinate2DInvalid;
}
- (NSString *)title
diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.h b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h
new file mode 100644
index 0000000000..6c01356944
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h
@@ -0,0 +1,11 @@
+#import <QuartzCore/QuartzCore.h>
+#import "MGLUserLocationAnnotationView.h"
+#import "MGLUserLocationHeadingIndicator.h"
+
+@interface MGLUserLocationHeadingArrowLayer : CAShapeLayer <MGLUserLocationHeadingIndicator>
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView;
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy;
+- (void)updateTintColor:(CGColorRef)color;
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.m b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m
new file mode 100644
index 0000000000..912ce30c35
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m
@@ -0,0 +1,59 @@
+#import "MGLUserLocationHeadingArrowLayer.h"
+
+#import "MGLFaux3DUserLocationAnnotationView.h"
+#import "MGLGeometry.h"
+
+const CGFloat MGLUserLocationHeadingArrowSize = 6;
+
+@implementation MGLUserLocationHeadingArrowLayer
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView
+{
+ CGFloat size = userLocationView.bounds.size.width + MGLUserLocationHeadingArrowSize;
+
+ self = [super init];
+ self.bounds = CGRectMake(0, 0, size, size);
+ self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds));
+ self.path = [self arrowPath];
+ self.fillColor = userLocationView.tintColor.CGColor;
+ self.shouldRasterize = YES;
+ self.rasterizationScale = UIScreen.mainScreen.scale;
+ self.drawsAsynchronously = YES;
+
+ self.strokeColor = UIColor.whiteColor.CGColor;
+ self.lineWidth = 1.0;
+ self.lineJoin = kCALineJoinRound;
+
+ return self;
+}
+
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy
+{
+ // unimplemented
+}
+
+- (void)updateTintColor:(CGColorRef)color
+{
+ self.fillColor = color;
+}
+
+- (CGPathRef)arrowPath {
+ CGFloat center = roundf(CGRectGetMidX(self.bounds));
+ CGFloat size = MGLUserLocationHeadingArrowSize;
+
+ CGPoint top = CGPointMake(center, 0);
+ CGPoint left = CGPointMake(center - size, size);
+ CGPoint right = CGPointMake(center + size, size);
+ CGPoint middle = CGPointMake(center, size / M_PI);
+
+ UIBezierPath *bezierPath = [UIBezierPath bezierPath];
+ [bezierPath moveToPoint:top];
+ [bezierPath addLineToPoint:left];
+ [bezierPath addQuadCurveToPoint:right controlPoint:middle];
+ [bezierPath addLineToPoint:top];
+ [bezierPath closePath];
+
+ return bezierPath.CGPath;
+}
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.h b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h
new file mode 100644
index 0000000000..93f8ea17ab
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h
@@ -0,0 +1,11 @@
+#import <QuartzCore/QuartzCore.h>
+#import "MGLUserLocationAnnotationView.h"
+#import "MGLUserLocationHeadingIndicator.h"
+
+@interface MGLUserLocationHeadingBeamLayer : CALayer <MGLUserLocationHeadingIndicator>
+
+- (MGLUserLocationHeadingBeamLayer *)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView;
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy;
+- (void)updateTintColor:(CGColorRef)color;
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.m b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m
new file mode 100644
index 0000000000..efe7e4db93
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m
@@ -0,0 +1,104 @@
+#import "MGLUserLocationHeadingBeamLayer.h"
+
+#import "MGLFaux3DUserLocationAnnotationView.h"
+#import "MGLGeometry.h"
+
+@implementation MGLUserLocationHeadingBeamLayer
+{
+ CAShapeLayer *_maskLayer;
+}
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView
+{
+ CGFloat size = MGLUserLocationAnnotationHaloSize;
+
+ self = [super init];
+ self.bounds = CGRectMake(0, 0, size, size);
+ self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds));
+ self.contents = (__bridge id)[self gradientImageWithTintColor:userLocationView.tintColor.CGColor];
+ self.contentsGravity = kCAGravityBottom;
+ self.contentsScale = UIScreen.mainScreen.scale;
+ self.opacity = 0.4;
+ self.shouldRasterize = YES;
+ self.rasterizationScale = UIScreen.mainScreen.scale;
+ self.drawsAsynchronously = YES;
+
+ _maskLayer = [CAShapeLayer layer];
+ _maskLayer.frame = self.bounds;
+ _maskLayer.path = [self clippingMaskForAccuracy:0];
+ self.mask = _maskLayer;
+
+ return self;
+}
+
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy
+{
+ // recalculate the clipping mask based on updated accuracy
+ _maskLayer.path = [self clippingMaskForAccuracy:accuracy];
+}
+
+- (void)updateTintColor:(CGColorRef)color
+{
+ // redraw the raw tinted gradient
+ self.contents = (__bridge id)[self gradientImageWithTintColor:color];
+}
+
+- (CGImageRef)gradientImageWithTintColor:(CGColorRef)tintColor
+{
+ UIImage *image;
+
+ CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0;
+
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0);
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGContextRef context = UIGraphicsGetCurrentContext();
+
+ // gradient from the tint color to no-alpha tint color
+ CGFloat gradientLocations[] = {0.0, 1.0};
+ CGGradientRef gradient = CGGradientCreateWithColors(
+ colorSpace,
+ (__bridge CFArrayRef)@[(__bridge id)tintColor,
+ (id)CFBridgingRelease(CGColorCreateCopyWithAlpha(tintColor, 0))],
+ gradientLocations);
+
+ // draw the gradient from the center point to the edge (full halo radius)
+ CGPoint centerPoint = CGPointMake(haloRadius, haloRadius);
+ CGContextDrawRadialGradient(context, gradient,
+ centerPoint, 0.0,
+ centerPoint, haloRadius,
+ kNilOptions);
+
+ image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ CGGradientRelease(gradient);
+ CGColorSpaceRelease(colorSpace);
+
+ return image.CGImage;
+}
+
+- (CGPathRef)clippingMaskForAccuracy:(CGFloat)accuracy
+{
+ // size the mask using accuracy, but keep within a good display range
+ CGFloat clippingDegrees = 90 - accuracy;
+ clippingDegrees = fmin(clippingDegrees, 70); // most accurate
+ clippingDegrees = fmax(clippingDegrees, 10); // least accurate
+
+ CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize);
+ UIBezierPath *ovalPath = UIBezierPath.bezierPath;
+
+ // clip the oval to ± incoming accuracy degrees (converted to radians), from the top
+ [ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))
+ radius:CGRectGetWidth(ovalRect) / 2.0
+ startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees)
+ endAngle:MGLRadiansFromDegrees(-clippingDegrees)
+ clockwise:YES];
+
+ [ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))];
+ [ovalPath closePath];
+
+ return ovalPath.CGPath;
+}
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingIndicator.h b/platform/ios/src/MGLUserLocationHeadingIndicator.h
new file mode 100644
index 0000000000..61476b96a2
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingIndicator.h
@@ -0,0 +1,10 @@
+#import <QuartzCore/QuartzCore.h>
+#import "MGLUserLocationAnnotationView.h"
+
+@protocol MGLUserLocationHeadingIndicator <NSObject>
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView;
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy;
+- (void)updateTintColor:(CGColorRef)color;
+
+@end
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index abe16cc3ee..9b2c472cf6 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -60,3 +60,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "NSValue+MGLAdditions.h"
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
+#import "MGLMapSnapshotter.h"
diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h
index 6e15e07cb5..22bb740242 100644
--- a/platform/ios/src/UIImage+MGLAdditions.h
+++ b/platform/ios/src/UIImage+MGLAdditions.h
@@ -8,6 +8,8 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage;
+- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale;
+
- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier;
- (mbgl::PremultipliedImage)mgl_premultipliedImage;
diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm
index 5e28d18190..8ab1d5c259 100644
--- a/platform/ios/src/UIImage+MGLAdditions.mm
+++ b/platform/ios/src/UIImage+MGLAdditions.mm
@@ -22,6 +22,19 @@
return self;
}
+- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale
+{
+ CGImageRef image = CGImageFromMGLPremultipliedImage(mbglImage.clone());
+ if (!image) {
+ return nil;
+ }
+
+ self = [self initWithCGImage:image scale:scale orientation:UIImageOrientationUp];
+
+ CGImageRelease(image);
+ return self;
+}
+
- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier {
BOOL isTemplate = self.renderingMode == UIImageRenderingModeAlwaysTemplate;
return std::make_unique<mbgl::style::Image>([identifier UTF8String],
diff --git a/platform/ios/uitest/KIF b/platform/ios/uitest/KIF
-Subproject 973a4cb653b54c3e8b2c0681f4097568ff0ac34
+Subproject c40b1048a6f35c6fd90376846a1a933844516b3
diff --git a/platform/ios/uitest/OHHTTPStubs b/platform/ios/uitest/OHHTTPStubs
-Subproject deed01a1592210a4c37f3f5c5f2b32fe0e41c60
+Subproject 4dc6f36375f78c0b3cfe58d90bb8a4e21df5196
diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake
index 3aa1fdbbfc..47c4c68806 100644
--- a/platform/linux/config.cmake
+++ b/platform/linux/config.cmake
@@ -8,12 +8,10 @@ mason_use(libpng VERSION 1.6.25)
mason_use(libjpeg-turbo VERSION 1.5.0)
mason_use(webp VERSION 0.5.1)
mason_use(gtest VERSION 1.8.0${MASON_CXXABI_SUFFIX})
-mason_use(benchmark VERSION 1.0.0-1)
+mason_use(benchmark VERSION 1.2.0)
mason_use(icu VERSION 58.1-min-size)
-# Link with libuv. This is not part of loop-uv.cmake because loop-uv.cmake is also
-# used by node.cmake, where we want to link with the libuv provided by node itself.
-target_add_mason_package(mbgl-loop-uv PUBLIC libuv)
+include(cmake/loop-uv.cmake)
macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PUBLIC mesa)
@@ -48,22 +46,6 @@ macro(mbgl_platform_core)
endif()
target_sources(mbgl-core
- # File source
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/http_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
-
# Misc
PRIVATE platform/default/logging_stderr.cpp
PRIVATE platform/default/string_stdlib.cpp
@@ -96,7 +78,6 @@ macro(mbgl_platform_core)
PRIVATE platform/default
)
- target_add_mason_package(mbgl-core PUBLIC sqlite)
target_add_mason_package(mbgl-core PUBLIC nunicode)
target_add_mason_package(mbgl-core PUBLIC libpng)
target_add_mason_package(mbgl-core PUBLIC libjpeg-turbo)
@@ -105,6 +86,22 @@ macro(mbgl_platform_core)
target_link_libraries(mbgl-core
PUBLIC -lz
+ )
+endmacro()
+
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/default/http_file_source.cpp
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_add_mason_package(mbgl-filesource PUBLIC sqlite)
+
+ target_link_libraries(mbgl-filesource
PUBLIC -lcurl
)
endmacro()
@@ -112,9 +109,12 @@ endmacro()
macro(mbgl_platform_glfw)
target_link_libraries(mbgl-glfw
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+ target_add_mason_package(mbgl-glfw PUBLIC libuv)
+
add_custom_command(
TARGET mbgl-glfw POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
@@ -126,15 +126,21 @@ endmacro()
macro(mbgl_platform_render)
target_link_libraries(mbgl-render
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+
+ target_add_mason_package(mbgl-render PUBLIC libuv)
endmacro()
macro(mbgl_platform_offline)
target_link_libraries(mbgl-offline
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+
+ target_add_mason_package(mbgl-offline PUBLIC libuv)
endmacro()
@@ -150,8 +156,11 @@ macro(mbgl_platform_test)
)
target_link_libraries(mbgl-test
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+
+ target_add_mason_package(mbgl-test PUBLIC libuv)
endmacro()
@@ -167,8 +176,11 @@ macro(mbgl_platform_benchmark)
)
target_link_libraries(mbgl-benchmark
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+
+ target_add_mason_package(mbgl-benchmark PUBLIC libuv)
endmacro()
diff --git a/platform/linux/src/headless_display_egl.cpp b/platform/linux/src/headless_display_egl.cpp
index 03c8e16a59..b746211924 100644
--- a/platform/linux/src/headless_display_egl.cpp
+++ b/platform/linux/src/headless_display_egl.cpp
@@ -32,6 +32,9 @@ HeadlessDisplay::Impl::Impl() {
}
const EGLint attribs[] = {
+#if MBGL_USE_GLES2
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+#endif
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE
};
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 29e3803dc2..31b3540a7c 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -2,11 +2,47 @@
## master
+### Networking and storage
+
+* Added a new `MGLMapSnapshotter` class for capturing rendered map images from an `MGLMapView`’s camera. ([#9891](https://github.com/mapbox/mapbox-gl-native/pull/9891))
+* Reduced the time it takes to create new `MGLMapView` instances in some cases. ([#9864](https://github.com/mapbox/mapbox-gl-native/pull/9864))
+* Added support for forced cache revalidation that will eliminate flickering that was sometimes visible for certain types of tiles (e.g., traffic tiles). ([#9670](https://github.com/mapbox/mapbox-gl-native/pull/9670), [#9103](https://github.com/mapbox/mapbox-gl-native/issues/9103))
+* Improved the performance of the SDK when parsing vector tile data used to render the map. ([#9312](https://github.com/mapbox/mapbox-gl-native/pull/9312))
+
### Styles
-* Added support for displaying geo-referenced images via the `MGLImageSource`. [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
-* The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+* Added a new type of source, represented by the `MGLImageSource` class at runtime, that displays a georeferenced image. ([#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110))
+* Setting a style using `MGLMapView`'s `styleURL` property now smoothly transitions from the previous style to the new style and maintains equivalent layers and sources along with their identifiers. ([#9256](https://github.com/mapbox/mapbox-gl-native/pull/9256))
+* Added `MGLCircleStyleLayer.circlePitchAlignment` and `MGLSymbolStyleLayer.iconPitchAlignment` properties to control whether circles and symbols lie flat against a tilted map. ([#9426](https://github.com/mapbox/mapbox-gl-native/pull/9426), [#9479](https://github.com/mapbox/mapbox-gl-native/pull/9479))
+* Added an `MGLSymbolStyleLayer.iconAnchor` property to control where an icon is anchored. ([#9849](https://github.com/mapbox/mapbox-gl-native/pull/9849))
+* The `maximumTextWidth` and `textLetterSpacing` properties of `MGLSymbolStyleLayer` are now compatible with `MGLSourceStyleFunction`s and `MGLCompositeStyleFunction`s, allowing data-driven styling of these properties. ([#9870](https://github.com/mapbox/mapbox-gl-native/pull/9870))
+* Improved the legibility of labels that follow lines when the map is tilted. ([#9009](https://github.com/mapbox/mapbox-gl-native/pull/9009))
+* Fixed an issue that could cause flickering when a translucent raster style layer was present. ([#9468](https://github.com/mapbox/mapbox-gl-native/pull/9468))
+* Fixed an issue that could cause antialiasing between polygons on the same layer to fail if the fill layers used data-driven styling for the fill color. ([#9699](https://github.com/mapbox/mapbox-gl-native/pull/9699))
+* The previously deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+
+### Annotations and user interaction
+
+* Fixed several bugs and performance issues related to the use of annotations backed by `MGLAnnotationImage`s. The limits on the number and size of images and glyphs has been effectively eliminated and should now depend on hardware constraints. These fixes also apply to images used to represent icons in `MGLSymbolStyleLayer`s. ([#9213](https://github.com/mapbox/mapbox-gl-native/pull/9213))
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
+* Added `-[MGLMapView cameraThatFitsShape:direction:edgePadding:]` to get a camera with zoom level and center coordinate computed to fit a shape. ([#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107))
+
+### Other changes
+
+* Fixed an issue that could cause line label rendering glitches when the line geometry is projected to a point behind the plane of the camera. ([#9865](https://github.com/mapbox/mapbox-gl-native/pull/9865))
+* Fixed an issue that could cause a crash when using `-[MGLMapView flyToCamera:completionHandler:]` and related methods with zoom levels at or near the maximum value. ([#9381](https://github.com/mapbox/mapbox-gl-native/pull/9381))
+
+## 0.5.1
+
+This version of the Mapbox macOS SDK corresponds to version 3.6.4 of the Mapbox iOS SDK.
+
+* Added an `MGLStyle.localizesLabels` property, off by default, that localizes any Mapbox Streets–sourced symbol layer into the user’s preferred language. ([#9582](https://github.com/mapbox/mapbox-gl-native/pull/9582))
+* Fixed an issue that caused `-[MGLShapeSource featuresMatchingPredicate:]` and `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` to always return an empty array. ([#9784](https://github.com/mapbox/mapbox-gl-native/pull/9784))
+* `MGLMapView`’s `minimumZoomLevel` and `maximumZoomLevel` properties are now available in Interface Builder’s Attributes inspector. ([#9729](https://github.com/mapbox/mapbox-gl-native/pull/9729))
+* Added a Hungarian localization. ([#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945))
+* Deprecated `+[MGLStyle trafficDayStyleURL]` and `+[MGLStyle trafficNightStyleURL]` with no replacement method. To use the Traffic Day and Traffic Night styles going forward, we recommend that you use the underlying URL. ([#9918](https://github.com/mapbox/mapbox-gl-native/pull/9918))
+* Fixed an issue where stale (but still valid) map data could be ignored in offline mode. ([#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012))
## 0.5.0
diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec
index 5b70c52681..dd60957bb5 100644
--- a/platform/macos/Mapbox-macOS-SDK-symbols.podspec
+++ b/platform/macos/Mapbox-macOS-SDK-symbols.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '0.5.0'
+ version = '0.5.1'
m.name = 'Mapbox-macOS-SDK-symbols'
m.version = "#{version}-symbols"
diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec
index 0b26b13beb..533d7499f5 100644
--- a/platform/macos/Mapbox-macOS-SDK.podspec
+++ b/platform/macos/Mapbox-macOS-SDK.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '0.5.0'
+ version = '0.5.1'
m.name = 'Mapbox-macOS-SDK'
m.version = version
diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib
index 9a8cf05c16..3243838848 100644
--- a/platform/macos/app/Base.lproj/MainMenu.xib
+++ b/platform/macos/app/Base.lproj/MainMenu.xib
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -128,6 +128,12 @@
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
+ <menuItem title="Export Image…" id="vjX-0E-kLO">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="takeSnapshot:" target="-1" id="H06-sU-n4U"/>
+ </connections>
+ </menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<connections>
@@ -654,7 +660,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="131" width="350" height="84"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="eA4-n3-qPe">
<rect key="frame" x="0.0" y="0.0" width="350" height="84"/>
<autoresizingMask key="autoresizingMask"/>
@@ -730,7 +736,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="830" y="430" width="400" height="300"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="8ha-hw-zOD">
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
<autoresizingMask key="autoresizingMask"/>
@@ -739,7 +745,7 @@ CA
<rect key="frame" x="-1" y="20" width="402" height="281"/>
<clipView key="contentView" id="J9U-Yx-o2S">
<rect key="frame" x="1" y="0.0" width="400" height="280"/>
- <autoresizingMask key="autoresizingMask"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" headerView="MAZ-Iq-hBi" id="Ato-Vu-HYT">
<rect key="frame" x="0.0" y="0.0" width="423" height="257"/>
diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib
index d95f21b2e9..0394f38533 100644
--- a/platform/macos/app/Base.lproj/MapDocument.xib
+++ b/platform/macos/app/Base.lproj/MapDocument.xib
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -48,7 +48,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" fullSizeContentView="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="388" y="211" width="642" height="480"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="TuG-C5-zLS">
<rect key="frame" x="0.0" y="0.0" width="642" height="480"/>
<autoresizingMask key="autoresizingMask"/>
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 94bf18dea1..feef53062b 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -3,9 +3,10 @@
#import "AppDelegate.h"
#import "LimeGreenStyleLayer.h"
#import "DroppedPinAnnotation.h"
+#import "MGLMapsnapshotter.h"
#import "MGLStyle+MBXAdditions.h"
-#import "MGLVectorSource+MBXAdditions.h"
+#import "MGLVectorSource+MGLAdditions.h"
#import <Mapbox/Mapbox.h>
@@ -73,6 +74,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
BOOL _isTouringWorld;
BOOL _isShowingPolygonAndPolylineAnnotations;
BOOL _isShowingAnimatedAnnotation;
+
+ // Snapshotter
+ MGLMapSnapshotter* snapshotter;
}
#pragma mark Lifecycle
@@ -153,6 +157,66 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
camera.heading, camera.pitch]];
}
+#pragma mark File methods
+
+- (IBAction)takeSnapshot:(id)sender {
+ MGLMapCamera *camera = self.mapView.camera;
+
+ MGLMapSnapshotOptions *options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:self.mapView.styleURL camera:camera size:self.mapView.bounds.size];
+ options.zoomLevel = self.mapView.zoomLevel;
+
+ // Create and start the snapshotter
+ snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options];
+ [snapshotter startWithCompletionHandler:^(NSImage *image, NSError *error) {
+ if (error) {
+ NSLog(@"Could not load snapshot: %@", error.localizedDescription);
+ } else {
+ // Set the default name for the file and show the panel.
+ NSSavePanel *panel = [NSSavePanel savePanel];
+ panel.nameFieldStringValue = [self.mapView.styleURL.lastPathComponent.stringByDeletingPathExtension stringByAppendingPathExtension:@"png"];
+ panel.allowedFileTypes = [@[(NSString *)kUTTypePNG] arrayByAddingObjectsFromArray:[NSBitmapImageRep imageUnfilteredTypes]];
+
+ [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
+ if (result == NSFileHandlingPanelOKButton) {
+ // Write the contents in the new format.
+ NSURL *fileURL = panel.URL;
+
+ NSBitmapImageRep *bitmapRep;
+ for (NSImageRep *imageRep in image.representations) {
+ if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
+ bitmapRep = (NSBitmapImageRep *)imageRep;
+ break; // stop on first bitmap rep we find
+ }
+ }
+
+ if (!bitmapRep) {
+ bitmapRep = [NSBitmapImageRep imageRepWithData:image.TIFFRepresentation];
+ }
+
+ CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileURL.pathExtension, NULL /* inConformingToUTI */);
+ NSBitmapImageFileType fileType = NSTIFFFileType;
+ if (UTTypeConformsTo(uti, kUTTypePNG)) {
+ fileType = NSPNGFileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeGIF)) {
+ fileType = NSGIFFileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeJPEG2000)) {
+ fileType = NSJPEG2000FileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeJPEG)) {
+ fileType = NSJPEGFileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeBMP)) {
+ fileType = NSBitmapImageFileTypeBMP;
+ }
+
+ NSData *imageData = [bitmapRep representationUsingType:fileType properties:@{}];
+ [imageData writeToURL:fileURL atomically:NO];
+ }
+ }];
+
+ }
+ snapshotter = nil;
+ }];
+}
+
#pragma mark View methods
- (IBAction)showStyle:(id)sender {
@@ -183,10 +247,10 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
styleURL = [MGLStyle satelliteStreetsStyleURL];
break;
case 7:
- styleURL = [MGLStyle trafficDayStyleURL];
+ styleURL = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"];
break;
case 8:
- styleURL = [MGLStyle trafficNightStyleURL];
+ styleURL = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"];
break;
default:
NSAssert(NO, @"Cannot set style from control with tag %li", (long)tag);
@@ -344,52 +408,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
- (void)updateLabels {
- MGLStyle *style = self.mapView.style;
- NSString *preferredLanguage = _isLocalizingLabels ? [MGLVectorSource preferredMapboxStreetsLanguage] : nil;
- NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary];
- for (MGLSymbolStyleLayer *layer in style.layers) {
- if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
- continue;
- }
-
- MGLVectorSource *source = (MGLVectorSource *)[style sourceWithIdentifier:layer.sourceIdentifier];
- if (![source isKindOfClass:[MGLVectorSource class]] || !source.mapboxStreets) {
- continue;
- }
-
- NSDictionary *localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier];
- if (!localizedKeysByKey) {
- localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier] = [source localizedKeysByKeyForPreferredLanguage:preferredLanguage];
- }
-
- NSString *(^stringByLocalizingString)(NSString *) = ^ NSString * (NSString *string) {
- NSMutableString *localizedString = string.mutableCopy;
- [localizedKeysByKey enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull localizedKey, BOOL * _Nonnull stop) {
- NSAssert([key isKindOfClass:[NSString class]], @"key is not a string");
- NSAssert([localizedKey isKindOfClass:[NSString class]], @"localizedKey is not a string");
- [localizedString replaceOccurrencesOfString:[NSString stringWithFormat:@"{%@}", key]
- withString:[NSString stringWithFormat:@"{%@}", localizedKey]
- options:0
- range:NSMakeRange(0, localizedString.length)];
- }];
- return localizedString;
- };
-
- if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
- NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue];
- layer.text = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)];
- }
- else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
- NSMutableDictionary *stops = function.stops.mutableCopy;
- [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) {
- NSString *textField = stop.rawValue;
- stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)];
- }];
- function.stops = stops;
- layer.text = function;
- }
- }
+ self.mapView.style.localizesLabels = _isLocalizingLabels;
}
- (void)applyPendingState {
@@ -854,10 +873,10 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
state = [styleURL isEqual:[MGLStyle satelliteStreetsStyleURL]];
break;
case 7:
- state = [styleURL isEqual:[MGLStyle trafficDayStyleURL]];
+ state = [styleURL isEqual:[NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"]];
break;
case 8:
- state = [styleURL isEqual:[MGLStyle trafficNightStyleURL]];
+ state = [styleURL isEqual:[NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"]];
break;
default:
return NO;
@@ -1008,6 +1027,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(giveFeedback:)) {
return YES;
}
+ if (menuItem.action == @selector(takeSnapshot:)) {
+ return !(snapshotter && [snapshotter isLoading]);
+ }
return NO;
}
diff --git a/platform/macos/app/hu.lproj/Localizable.strings b/platform/macos/app/hu.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/macos/app/hu.lproj/Localizable.strings
diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake
index 86c54b612c..aca99f9b40 100644
--- a/platform/macos/config.cmake
+++ b/platform/macos/config.cmake
@@ -3,32 +3,14 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10)
mason_use(glfw VERSION 2017-07-13-67c9155)
mason_use(boost_libprogram_options VERSION 1.62.0)
mason_use(gtest VERSION 1.8.0)
-mason_use(benchmark VERSION 1.0.0-1)
+mason_use(benchmark VERSION 1.2.0)
mason_use(icu VERSION 58.1-min-size)
+include(cmake/loop-uv.cmake)
include(cmake/loop-darwin.cmake)
macro(mbgl_platform_core)
target_sources(mbgl-core
- # File source
- PRIVATE platform/darwin/src/http_file_source.mm
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Default styles
- PRIVATE platform/default/mbgl/util/default_styles.hpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
-
# Misc
PRIVATE platform/darwin/mbgl/storage/reachability.h
PRIVATE platform/darwin/mbgl/storage/reachability.m
@@ -53,6 +35,10 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/gl/headless_display.hpp
PRIVATE platform/darwin/src/headless_display_cgl.cpp
+ # Snapshotting
+ PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.hpp
+
# Thread pool
PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp
@@ -82,23 +68,47 @@ macro(mbgl_platform_core)
PUBLIC "-framework ImageIO"
PUBLIC "-framework CoreServices"
PUBLIC "-framework SystemConfiguration"
+ )
+endmacro()
+
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/darwin/src/http_file_source.mm
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_compile_options(mbgl-filesource
+ PRIVATE -fobjc-arc
+ PRIVATE -fvisibility=hidden
+ )
+
+ target_link_libraries(mbgl-filesource
PUBLIC "-lsqlite3"
+ PUBLIC "-framework Foundation"
)
endmacro()
macro(mbgl_platform_glfw)
target_link_libraries(mbgl-glfw
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
+
target_compile_options(mbgl-glfw
PRIVATE -fvisibility=hidden
)
+
endmacro()
macro(mbgl_platform_render)
target_link_libraries(mbgl-render
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
target_compile_options(mbgl-render
@@ -109,6 +119,7 @@ endmacro()
macro(mbgl_platform_offline)
target_link_libraries(mbgl-offline
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
target_compile_options(mbgl-offline
@@ -133,6 +144,7 @@ macro(mbgl_platform_test)
)
target_link_libraries(mbgl-test
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
endmacro()
@@ -153,6 +165,7 @@ macro(mbgl_platform_benchmark)
)
target_link_libraries(mbgl-benchmark
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
endmacro()
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 5b4cadbc7c..34f8860686 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -16,6 +16,8 @@
1F7454A51ECFB00300021D39 /* MGLLight.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A21ECFB00300021D39 /* MGLLight.mm */; };
1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */; };
1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */; };
+ 1FCDF1421F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */; };
+ 1FCDF1431F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */; };
30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; };
3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; };
3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; };
@@ -78,7 +80,11 @@
556660D61E1D07E400E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D51E1D07E400E2C41B /* MGLVersionNumber.m */; };
558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */; };
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE7A51E56161C00C7916D /* MGLFoundation.mm */; };
+ 55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
+ 55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */; };
55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */; };
+ 92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */; };
920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */; };
92F2C3EB1F0E3A1900268EC0 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */; };
96E027311E57C9A7004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027331E57C9A7004B8E66 /* Localizable.strings */; };
@@ -237,7 +243,6 @@
DAEDC4371D606291000224FF /* MGLAttributionButtonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */; };
DAF0D80E1DFE0E5D00B28378 /* MGLPointCollection_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */; };
DAF0D8161DFE6B1800B28378 /* MGLAttributionInfo_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */; };
- DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */; };
DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */; };
DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */; };
DD58A4C91D822C6700E1F038 /* MGLExpressionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */; };
@@ -284,6 +289,8 @@
1F7454A21ECFB00300021D39 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = "<group>"; };
1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLightTest.mm; sourceTree = "<group>"; };
1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; };
+ 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MGLAdditions.h"; sourceTree = "<group>"; };
+ 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MGLAdditions.m"; sourceTree = "<group>"; };
30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+MGLAdditions.h"; path = "src/NSImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = "<group>"; };
3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSExpression+MGLAdditions.mm"; sourceTree = "<group>"; };
@@ -348,9 +355,12 @@
556660D51E1D07E400E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = "<group>"; };
558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = "<group>"; };
558DE7A51E56161C00C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = "<group>"; };
+ 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
55D9B4B01D005D3900C1CCE2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = "<group>"; };
55FE0E8D1D100A0900FD240B /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/macos/config.xcconfig; sourceTree = "<group>"; };
+ 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; };
+ 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; };
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLSourceQueryTests.m; sourceTree = "<group>"; };
92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererFrontend.h; sourceTree = "<group>"; };
966091701E5BBFF700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -395,21 +405,25 @@
DA6023EF1E4CE8E500DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Foundation.strings; sourceTree = "<group>"; };
DA6023F01E4CE8FF00DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B131E68850300CB7F44 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
- DA618B141E68852C00CB7F44 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA618B141E68852C00CB7F44 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B151E6886DF00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ca; path = ca.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B161E6886E000CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B171E68876C00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Foundation.strings; sourceTree = "<group>"; };
- DA618B181E6887C600CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA618B181E6887C600CB7F44 /* ca */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B231E6891ED00CB7F44 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Foundation.strings; sourceTree = "<group>"; };
DA618B241E6891F300CB7F44 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B271E68926E00CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B2A1E6892B500CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
DA6408D51DA4E5DA00908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = "<group>"; };
DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = "<group>"; };
+ DA704CBA1F6372E8004B3F28 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA704CBE1F637531004B3F28 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA704CBF1F637548004B3F28 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA704CC61F666385004B3F28 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Foundation.strings; sourceTree = "<group>"; };
DA7262051DEEDD460043BB89 /* MGLOpenGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOpenGLStyleLayer.h; sourceTree = "<group>"; };
DA7262061DEEDD460043BB89 /* MGLOpenGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOpenGLStyleLayer.mm; sourceTree = "<group>"; };
DA737ADE1E5914AD00AD2CDE /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
- DA737ADF1E5914D300AD2CDE /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA737ADF1E5914D300AD2CDE /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
DA737AE31E5915A500AD2CDE /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pl; path = pl.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA737AE41E5915B000AD2CDE /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
DA737AEC1E59180E00AD2CDE /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -469,7 +483,7 @@
DAA32CB31E4C4CC3006F8D24 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CBA1E4C4F10006F8D24 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CC01E4C4F89006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Foundation.strings; sourceTree = "<group>"; };
- DAA32CC11E4C4F93006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DAA32CC11E4C4F93006F8D24 /* vi */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleLayerIconTransformer.h; sourceTree = "<group>"; };
DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StyleLayerIconTransformer.m; sourceTree = "<group>"; };
DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = "<group>"; };
@@ -483,6 +497,8 @@
DACC22121CF3D3E200D220D9 /* MGLFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature.h; sourceTree = "<group>"; };
DACC22131CF3D3E200D220D9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = "<group>"; };
DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature_Private.h; sourceTree = "<group>"; };
+ DACCD9C71F1F443B00BB09A1 /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DACFE7971F66EA0C00630DA8 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DAD165721CF4CD7A001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = "<group>"; };
DAD165731CF4CD7A001FF4B9 /* MGLShapeCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeCollection.mm; sourceTree = "<group>"; };
DAE6C2E11CC304F900DB3429 /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
@@ -575,9 +591,7 @@
DAEDC4361D606291000224FF /* MGLAttributionButtonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAttributionButtonTests.m; sourceTree = "<group>"; };
DAF0D80D1DFE0E5D00B28378 /* MGLPointCollection_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection_Private.h; sourceTree = "<group>"; };
DAF0D8151DFE6B1800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = "<group>"; };
- DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MBXAdditions.h"; sourceTree = "<group>"; };
- DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MBXAdditions.m"; sourceTree = "<group>"; };
- DAFBD0D51E3FA969000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
+ DAFBD0D51E3FA969000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
DAFBD0D61E3FA983000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; };
DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = "<group>"; };
DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = "<group>"; };
@@ -598,6 +612,7 @@
buildActionMask = 2147483647;
files = (
5548BE781D09E718005DDE81 /* libmbgl-core.a in Frameworks */,
+ 55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */,
52B5D17F1E5E26DF00BBCB48 /* libmbgl-loop-darwin.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -607,6 +622,7 @@
buildActionMask = 2147483647;
files = (
DAE0DD7A1D5F015A005A6BB1 /* libmbgl-core.a in Frameworks */,
+ 55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */,
DAE6C3321CC30DB200DB3429 /* Mapbox.framework in Frameworks */,
52B5D1801E5E26DF00BBCB48 /* libmbgl-loop-darwin.a in Frameworks */,
);
@@ -749,8 +765,6 @@
DA839E9E1CC2E3400062CAFB /* MapDocument.xib */,
DACB0C371E18DFFD005DDBEA /* MGLStyle+MBXAdditions.h */,
DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */,
- DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */,
- DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */,
DAE6C2E91CC3050F00DB3429 /* OfflinePackNameValueTransformer.h */,
DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */,
DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */,
@@ -930,6 +944,8 @@
DAD1657F1CF4CF50001FF4B9 /* Categories */ = {
isa = PBXGroup;
children = (
+ 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */,
+ 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */,
408AA8601DAEED3300022900 /* MGLPolygon+MGLAdditions.h */,
408AA85C1DAEED3300022900 /* MGLPolygon+MGLAdditions.m */,
408AA8611DAEED3300022900 /* MGLPolyline+MGLAdditions.h */,
@@ -968,6 +984,7 @@
DAE6C31E1CC308BC00DB3429 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */,
5548BE7B1D0ACBBD005DDE81 /* libmbgl-loop-darwin.a */,
55D9B4B01D005D3900C1CCE2 /* libz.tbd */,
52BECB091CC5A26F009CD791 /* SystemConfiguration.framework */,
@@ -1046,6 +1063,8 @@
558DE7A51E56161C00C7916D /* MGLFoundation.mm */,
DAE6C34D1CC31E0400DB3429 /* MGLMapCamera.h */,
DAE6C36E1CC31E2A00DB3429 /* MGLMapCamera.mm */,
+ 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */,
+ 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */,
DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */,
DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */,
92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */,
@@ -1109,6 +1128,7 @@
DA8F259C1D51CB000010E6B5 /* MGLStyleValue_Private.h in Headers */,
DAE6C35B1CC31E0400DB3429 /* MGLAnnotation.h in Headers */,
DAE6C3B61CC31EF300DB3429 /* MGLMapView_Private.h in Headers */,
+ 92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */,
3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */,
DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */,
DAE6C3B21CC31EF300DB3429 /* MGLAttributionButton.h in Headers */,
@@ -1166,6 +1186,7 @@
DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */,
DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */,
DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */,
+ 1FCDF1421F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h in Headers */,
DAE6C3A61CC31E9400DB3429 /* MGLMapViewDelegate.h in Headers */,
DAE6C38B1CC31E2A00DB3429 /* MGLOfflinePack_Private.h in Headers */,
558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */,
@@ -1323,6 +1344,7 @@
ca,
fi,
nl,
+ hu,
);
mainGroup = DA839E891CC2E3400062CAFB;
productRefGroup = DA839E931CC2E3400062CAFB /* Products */;
@@ -1391,7 +1413,6 @@
DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */,
DA839E9A1CC2E3400062CAFB /* main.m in Sources */,
DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */,
- DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */,
DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1401,6 +1422,7 @@
buildActionMask = 2147483647;
files = (
07A019EF1ED665CD00ACD43E /* MGLImageSource.mm in Sources */,
+ 92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */,
40ABDB561DB0022100372083 /* NSImage+MGLAdditions.mm in Sources */,
DAE6C3901CC31E2A00DB3429 /* MGLPointAnnotation.mm in Sources */,
DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */,
@@ -1408,6 +1430,7 @@
40B77E461DB11BCD003DA2FE /* NSArray+MGLAdditions.mm in Sources */,
DAE6C38C1CC31E2A00DB3429 /* MGLOfflinePack.mm in Sources */,
35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */,
+ 1FCDF1431F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m in Sources */,
DD0902B21DB1AC6400C5BDCE /* MGLNetworkConfiguration.m in Sources */,
1F7454A51ECFB00300021D39 /* MGLLight.mm in Sources */,
DAE6C3B11CC31EF300DB3429 /* MGLAnnotationImage.m in Sources */,
@@ -1549,6 +1572,7 @@
DA618B161E6886E000CB7F44 /* ca */,
DA618B271E68926E00CB7F44 /* fi */,
DAE8CCAB1E6E8B72009B5CB0 /* nl */,
+ DA704CBE1F637531004B3F28 /* hu */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1597,6 +1621,7 @@
DA618B181E6887C600CB7F44 /* ca */,
DA618B2A1E6892B500CB7F44 /* fi */,
DAE8CCAC1E6E8B8D009B5CB0 /* nl */,
+ DA704CBF1F637548004B3F28 /* hu */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1614,6 +1639,9 @@
DA618B171E68876C00CB7F44 /* ca */,
DA618B231E6891ED00CB7F44 /* lt */,
DAE9E0F21EB7BF39001E8E8B /* es */,
+ DACCD9C71F1F443B00BB09A1 /* fr */,
+ DA704CBA1F6372E8004B3F28 /* ru */,
+ DA704CC61F666385004B3F28 /* uk */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -1632,6 +1660,7 @@
DAE8CCAA1E6E8605009B5CB0 /* ru */,
DA618B151E6886DF00CB7F44 /* ca */,
DA618B241E6891F300CB7F44 /* lt */,
+ DACFE7971F66EA0C00630DA8 /* vi */,
);
name = Foundation.stringsdict;
sourceTree = "<group>";
@@ -1795,7 +1824,10 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
+ );
INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
@@ -1806,7 +1838,7 @@
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_LDFLAGS = (
"$(mbgl_core_LINK_LIBRARIES)",
- "$(mbgl_loop_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = Mapbox;
@@ -1829,14 +1861,17 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
+ );
INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_LDFLAGS = (
"$(mbgl_core_LINK_LIBRARIES)",
- "$(mbgl_loop_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = Mapbox;
@@ -1853,7 +1888,10 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
+ );
INFOPLIST_FILE = test/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
@@ -1882,7 +1920,10 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
- HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
+ HEADER_SEARCH_PATHS = (
+ "$(mbgl_core_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
+ );
INFOPLIST_FILE = test/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
OTHER_CFLAGS = "-fvisibility=hidden";
diff --git a/platform/macos/sdk/ca.lproj/Localizable.strings b/platform/macos/sdk/ca.lproj/Localizable.strings
index b679fa0459..cd3073a8cb 100644
--- a/platform/macos/sdk/ca.lproj/Localizable.strings
+++ b/platform/macos/sdk/ca.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "El mapa no s’ha carregat a causa d’un error desconegut.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "El mapa no s’ha carregat perquè l’estil no es pot carregar.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "El mapa no s’ha carregat perquè s’ha corromput l’estil.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "El mapa no s’ha carregat perquè no es troba l’estil o bé és incompatible.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/es.lproj/Localizable.strings b/platform/macos/sdk/es.lproj/Localizable.strings
index 4ebb97b440..8a9b51feb1 100644
--- a/platform/macos/sdk/es.lproj/Localizable.strings
+++ b/platform/macos/sdk/es.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "No se pudo cargar el mapa debido a un error desconocido.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a un error de carga en el estilo.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a que el estilo está dañado.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "No se pudo cargar el mapa debido a que no se encuentra el estilo o está incompleto.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/fr.lproj/Localizable.strings b/platform/macos/sdk/fr.lproj/Localizable.strings
index 34e085ef2b..9d73f23d05 100644
--- a/platform/macos/sdk/fr.lproj/Localizable.strings
+++ b/platform/macos/sdk/fr.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "La carte n’a pas pu être chargée car une erreur inconnue est survenue.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style ne peut pas être chargé.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style est corrompu.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "La carte n’a pas pu être chargée car le style n’a pas été trouvé ou est incompatible.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/hu.lproj/Localizable.strings b/platform/macos/sdk/hu.lproj/Localizable.strings
new file mode 100644
index 0000000000..b3724190cb
--- /dev/null
+++ b/platform/macos/sdk/hu.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Egy ismeretlen hiba miatt nem sikerült betölteni a térképet.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílus nem tölthető be.";
+
+/* Accessibility title */
+"MAP_A11Y_TITLE" = "Mapbox";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílus hibás.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Nem sikerült betölteni a térképet, mert a stílus nem található vagy inkompatibilis.";
+
+/* Label of Zoom In button */
+"ZOOM_IN_LABEL" = "+";
+
+/* Tooltip of Zoom In button */
+"ZOOM_IN_TOOLTIP" = "Nagyítás";
+
+/* Label of Zoom Out button; U+2212 MINUS SIGN */
+"ZOOM_OUT_LABEL" = "−";
+
+/* Tooltip of Zoom Out button */
+"ZOOM_OUT_TOOLTIP" = "Kicsinyítés";
+
diff --git a/platform/macos/sdk/ru.lproj/Localizable.strings b/platform/macos/sdk/ru.lproj/Localizable.strings
index 4fa1104cc7..067e05b14e 100644
--- a/platform/macos/sdk/ru.lproj/Localizable.strings
+++ b/platform/macos/sdk/ru.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Не удалось загрузит карту из-за неизвестной ошибки.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту так как невозможно загрузить стиль.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки в стиле.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Не удалось загрузить карту так как стиль не найден или несовместим.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/uk.lproj/Localizable.strings b/platform/macos/sdk/uk.lproj/Localizable.strings
index 1be17a4d49..07782e81d5 100644
--- a/platform/macos/sdk/uk.lproj/Localizable.strings
+++ b/platform/macos/sdk/uk.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Неможливо завантажити мапу через невідому помилку.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, бо неможливо завантажити стиль.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, через помилки в стилі.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Неможливо завантажити мапу, бо неможливо знайти стиль або він несумісний.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/vi.lproj/Localizable.strings b/platform/macos/sdk/vi.lproj/Localizable.strings
index 086820b034..95e327d689 100644
--- a/platform/macos/sdk/vi.lproj/Localizable.strings
+++ b/platform/macos/sdk/vi.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Bản đồ bị thất bại khi tải vì lỗi không rõ.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Bản đồ bị thất bại khi tải vì không thể tải bảng kiểu.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Bản đồ bị thất bại khi tải vì bảng kiểu bị hỏng.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Bản đồ bị thất bại khi tải vì không tìm thấy bảng kiểu hoặc bảng kiểu không tương thích.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/zh-Hant.lproj/Localizable.strings b/platform/macos/sdk/zh-Hant.lproj/Localizable.strings
index 4447371d71..e5cf4b7695 100644
--- a/platform/macos/sdk/zh-Hant.lproj/Localizable.strings
+++ b/platform/macos/sdk/zh-Hant.lproj/Localizable.strings
@@ -1,6 +1,18 @@
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "發生不知名錯誤,無法載入地圖。";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "載入樣式表時發生錯誤,無法載入地圖。";
+
/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "樣式表有毀損,無法載入地圖。";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "找不到樣式表或樣式表不兼容,無法載入地圖。";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h
index 6bfdcfd100..de099157c8 100644
--- a/platform/macos/src/MGLMapView.h
+++ b/platform/macos/src/MGLMapView.h
@@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
@class MGLAnnotationImage;
@class MGLMapCamera;
@class MGLStyle;
+@class MGLShape;
@protocol MGLAnnotation;
@protocol MGLMapViewDelegate;
@@ -239,7 +240,7 @@ MGL_EXPORT IB_DESIGNABLE
The default value of this property is 0.
*/
-@property (nonatomic) double minimumZoomLevel;
+@property (nonatomic) IBInspectable double minimumZoomLevel;
/**
The maximum zoom level the map can be shown at.
@@ -247,9 +248,10 @@ MGL_EXPORT IB_DESIGNABLE
If the value of this property is smaller than that of the `minimumZoomLevel`
property, the behavior is undefined.
- The default value of this property is 20.
+ The default value of this property is 22. The upper bound for this property
+ is 25.5.
*/
-@property (nonatomic) double maximumZoomLevel;
+@property (nonatomic) IBInspectable double maximumZoomLevel;
/**
Changes the zoom level of the map and optionally animates the change.
@@ -321,6 +323,24 @@ MGL_EXPORT IB_DESIGNABLE
*/
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
+ /**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition duration and timing function.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously.
+ @param function A timing function used for the animation. Set this parameter to
+ `nil` for a transition that matches most system animations. If the duration
+ is `0`, this parameter is ignored.
+ @param edgePadding The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(NSEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion;
+
+
/**
Moves the viewpoint to a different location using a transition animation that
evokes powered flight and a default duration based on the length of the flight
@@ -456,6 +476,20 @@ MGL_EXPORT IB_DESIGNABLE
- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets;
/**
+ Returns the camera that best fits the given shape, with the specified direction,
+ optionally with some additional padding on each side.
+
+ @param shape The shape to fit to the receiver’s viewport.
+ @param direction The direction of the viewport, measured in degrees clockwise from true north.
+ @param insets The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @return A camera object centered on the shape's center with zoom level as high
+ (close to the ground) as possible while still including the entire shape. The
+ camera object uses the current pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(NSEdgeInsets)insets;
+
+/**
A Boolean value indicating whether the receiver automatically adjusts its
content insets.
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 5dbb134ef5..0aa5bdc9db 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -14,6 +14,7 @@
#import "MGLMultiPoint_Private.h"
#import "MGLOfflineStorage_Private.h"
#import "MGLStyle_Private.h"
+#import "MGLShape_Private.h"
#import "MGLAccountManager.h"
#import "MGLMapCamera.h"
@@ -1121,6 +1122,10 @@ public:
}
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion {
+ [self setCamera:camera withDuration:duration animationTimingFunction:function edgePadding:self.contentInsets completionHandler:completion];
+}
+
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(NSEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion {
mbgl::AnimationOptions animationOptions;
if (duration > 0) {
animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
@@ -1148,7 +1153,7 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
- mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera];
+ mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding];
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
}
@@ -1194,17 +1199,17 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
- mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera];
+ mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:self.contentInsets];
_mbglMap->flyTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
}
/// Returns a CameraOptions object that specifies parameters for animating to
/// the given camera.
-- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera {
+- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(NSEdgeInsets) edgePadding {
mbgl::CameraOptions options;
options.center = MGLLatLngFromLocationCoordinate2D(camera.centerCoordinate);
- options.padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets);
+ options.padding = MGLEdgeInsetsFromNSEdgeInsets(edgePadding);
options.zoom = MGLZoomLevelForAltitude(camera.altitude, camera.pitch,
camera.centerCoordinate.latitude,
self.frame.size);
@@ -1267,6 +1272,15 @@ public:
return [self cameraForCameraOptions:cameraOptions];
}
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(NSEdgeInsets)insets {
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets);
+
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForGeometry([shape geometryObject], padding, direction);
+
+ return [self cameraForCameraOptions:cameraOptions];
+}
+
- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions {
CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng());
double zoomLevel = cameraOptions.zoom ? *cameraOptions.zoom : self.zoomLevel;
@@ -2883,6 +2897,10 @@ public:
setViewport(0, 0, nativeView.framebufferSize);
}
+ mbgl::Size getFramebufferSize() const override {
+ return nativeView.framebufferSize;
+ }
+
mbgl::PremultipliedImage readStillImage() {
return readFramebuffer(nativeView.framebufferSize);
}
diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h
index e4ad258b6e..a082a4771e 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -56,3 +56,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "NSValue+MGLAdditions.h"
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
+#import "MGLMapSnapshotter.h"
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md
index bdcad8c0e0..58234dad0f 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,3 +1,12 @@
+# 3.5.7 - October 9, 2017
+- Fixed an issue causing synchronous resource requests to stall [#10153](https://github.com/mapbox/mapbox-gl-native/pull/10153)
+
+# 3.5.6 - September 29, 2017
+- Protects against requests which throw [#9554](https://github.com/mapbox/mapbox-gl-native/pull/9554)
+- Fixed an issue around reusing a map object [#9554](https://github.com/mapbox/mapbox-gl-native/pull/9554)
+- Fixed an issue in test [#9553](https://github.com/mapbox/mapbox-gl-native/pull/9553)
+- Increased the default maximum zoom level from 20 to 22 ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
# 3.5.5 - July 14, 2017
- Provide debuggable release builds for node packages [#9497](https://github.com/mapbox/mapbox-gl-native/pull/9497)
@@ -179,7 +188,7 @@
- Check libuv version semver-ishly, fixes segfaults in Node.js 0.12.x
and io.js.
-- Fixes segfault, throws JavaScript error instead when attempting to
+- Fixes segfault, throws JavaScript error instead when attempting to
render without first loading a style.
- Bumps mbgl submodule to v0.4.0
@@ -205,12 +214,12 @@
# 1.0.3 - April 3, 2015
-- Fixes crash during garbage collection by assigning FileSource handle
+- Fixes crash during garbage collection by assigning FileSource handle
to a v8::Persistent in NodeMap constructor.
# 1.0.2 - April 2, 2015
-- Initialize shared display connection at module load time to avoid
+- Initialize shared display connection at module load time to avoid
race condition when display connection is initialized on-demand.
# 1.0.1 - March 19, 2015
diff --git a/platform/node/scripts/after_success.sh b/platform/node/scripts/after_success.sh
index 18e00a4f0d..a5c3c5ec36 100755
--- a/platform/node/scripts/after_success.sh
+++ b/platform/node/scripts/after_success.sh
@@ -3,7 +3,9 @@
set -e
set -o pipefail
-if [[ "${PUBLISH:-}" == true ]]; then
+PACKAGE_JSON_VERSION=`node -e "console.log(require('./package.json').version)"`
+
+if [[ "${CIRCLE_TAG}" == "node-v${PACKAGE_JSON_VERSION}" ]] || [[ "${PUBLISH:-}" == true ]]; then
if [[ "${BUILDTYPE}" == "RelWithDebInfo" ]]; then
./node_modules/.bin/node-pre-gyp package publish info
elif [[ "${BUILDTYPE}" == "Debug" ]]; then
diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp
index 9bae86b76a..8a3927e2cf 100644
--- a/platform/node/src/node_geojson.hpp
+++ b/platform/node/src/node_geojson.hpp
@@ -1,13 +1,15 @@
#include <mbgl/style/conversion/geojson.hpp>
+#include <string>
namespace mbgl {
namespace style {
namespace conversion {
template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>&, Error& error) const {
- error = { "not implemented" };
- return {};
+optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>& value, Error& error) const {
+ Nan::JSON JSON;
+ std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked());
+ return convert<GeoJSON>(string, error);
}
} // namespace conversion
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 7c7082bd09..9a7ebce445 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -53,6 +53,7 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
Nan::SetPrototypeMethod(tpl, "addSource", AddSource);
+ Nan::SetPrototypeMethod(tpl, "removeSource", RemoveSource);
Nan::SetPrototypeMethod(tpl, "addLayer", AddLayer);
Nan::SetPrototypeMethod(tpl, "removeLayer", RemoveLayer);
Nan::SetPrototypeMethod(tpl, "addImage", AddImage);
@@ -151,25 +152,15 @@ void NodeMap::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(info.This());
}
-std::string StringifyStyle(v8::Local<v8::Value> styleHandle) {
- Nan::HandleScope scope;
-
- v8::Local<v8::Object> JSON = Nan::To<v8::Object>(
- Nan::Get(
- Nan::GetCurrentContext()->Global(),
- Nan::New("JSON").ToLocalChecked()
- ).ToLocalChecked()
- ).ToLocalChecked();
-
- return *Nan::Utf8String(Nan::MakeCallback(JSON, "stringify", 1, &styleHandle));
-}
-
/**
* Load a stylesheet
*
* @function
* @name load
* @param {string|Object} stylesheet either an object or a JSON representation
+ * @param {Object} options
+ * @param {boolean} options.defaultStyleCamera if true, sets the default style
+ * camera
* @returns {undefined} loads stylesheet into map
* @throws {Error} if stylesheet is missing or invalid
* @example
@@ -194,7 +185,8 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
std::string style;
if (info[0]->IsObject()) {
- style = StringifyStyle(info[0]);
+ Nan::JSON JSON;
+ style = *Nan::Utf8String(JSON.Stringify(info[0]->ToObject()).ToLocalChecked());
} else if (info[0]->IsString()) {
style = *Nan::Utf8String(info[0]);
} else {
@@ -207,6 +199,21 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowError(ex.what());
}
+ if (info.Length() == 2) {
+ if (!info[1]->IsObject()) {
+ return Nan::ThrowTypeError("Second argument must be an options object");
+ }
+ auto options = Nan::To<v8::Object>(info[1]).ToLocalChecked();
+ if (Nan::Has(options, Nan::New("defaultStyleCamera").ToLocalChecked()).FromJust()) {
+ if (!Nan::Get(options, Nan::New("defaultStyleCamera").ToLocalChecked()).ToLocalChecked()->IsBoolean()) {
+ return Nan::ThrowError("Options object 'defaultStyleCamera' property must be a boolean");
+ }
+ if (Nan::Get(options, Nan::New("cameraMutated").ToLocalChecked()).ToLocalChecked()->BooleanValue()) {
+ nodeMap->map->jumpTo(nodeMap->map->getStyle().getDefaultCamera());
+ }
+ }
+ }
+
nodeMap->loaded = true;
info.GetReturnValue().SetUndefined();
@@ -357,28 +364,13 @@ void NodeMap::startRender(NodeMap::RenderOptions options) {
frontend->setSize(options.size);
map->setSize(options.size);
- if (map->getZoom() != options.zoom) {
- map->setZoom(options.zoom);
- }
-
- mbgl::LatLng latLng(options.latitude, options.longitude);
- if (map->getLatLng() != latLng) {
- map->setLatLng(latLng);
- }
+ mbgl::CameraOptions camera;
+ camera.center = mbgl::LatLng { options.latitude, options.longitude };
+ camera.zoom = options.zoom;
+ camera.angle = -options.bearing * mbgl::util::DEG2RAD;
+ camera.pitch = options.pitch * mbgl::util::DEG2RAD;
- if (map->getBearing() != options.bearing) {
- map->setBearing(options.bearing);
- }
-
- if (map->getPitch() != options.pitch) {
- map->setPitch(options.pitch);
- }
-
- if (map->getDebug() != options.debugOptions) {
- map->setDebug(options.debugOptions);
- }
-
- map->renderStill([this](const std::exception_ptr eptr) {
+ map->renderStill(camera, options.debugOptions, [this](const std::exception_ptr eptr) {
if (eptr) {
error = std::move(eptr);
uv_async_send(async);
@@ -542,6 +534,10 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
+ if (!info[1]->IsObject()) {
+ return Nan::ThrowTypeError("Second argument must be an object");
+ }
+
Error error;
mbgl::optional<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(info[1], error, *Nan::Utf8String(info[0]));
if (!source) {
@@ -552,6 +548,24 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
nodeMap->map->getStyle().addSource(std::move(*source));
}
+void NodeMap::RemoveSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() != 1) {
+ return Nan::ThrowTypeError("One argument required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ nodeMap->map->getStyle().removeSource(*Nan::Utf8String(info[0]));
+}
+
void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
using namespace mbgl::style;
using namespace mbgl::style::conversion;
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index 7bd3b99bea..c8e33bb347 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -45,6 +45,7 @@ public:
static void Release(const Nan::FunctionCallbackInfo<v8::Value>&);
static void Cancel(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddSource(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void RemoveSource(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddImage(const Nan::FunctionCallbackInfo<v8::Value>&);
diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json
index 470e84bedb..a4762badbc 100644
--- a/platform/node/test/ignores.json
+++ b/platform/node/test/ignores.json
@@ -9,10 +9,6 @@
"query-tests/symbol-features-in/tilted-outside": "https://github.com/mapbox/mapbox-gl-native/issues/9435",
"query-tests/world-wrapping/box": "skip - needs issue",
"query-tests/world-wrapping/point": "skip - needs issue",
- "render-tests/circle-pitch-alignment/map-scale-map": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
- "render-tests/circle-pitch-alignment/map-scale-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
- "render-tests/circle-pitch-alignment/viewport-scale-map": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
- "render-tests/circle-pitch-alignment/viewport-scale-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9349",
"render-tests/debug/collision-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
"render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
"render-tests/debug/collision-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
@@ -27,12 +23,6 @@
"render-tests/fill-extrusion-pattern/literal": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/missing": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
- "render-tests/fill-outline-color/fill": "https://github.com/mapbox/mapbox-gl-native/pull/8760",
- "render-tests/fill-outline-color/function": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
- "render-tests/fill-outline-color/literal": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
- "render-tests/fill-outline-color/multiply": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
- "render-tests/fill-outline-color/opacity": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
- "render-tests/fill-outline-color/zoom-and-property-function": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
"render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary",
"render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js",
"render-tests/icon-size/composite-function-high-base-plain": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
@@ -42,19 +32,45 @@
"render-tests/icon-text-fit/height": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
"render-tests/icon-text-fit/width-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
"render-tests/icon-text-fit/width": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/line-width/property-function": "https://github.com/mapbox/mapbox-gl-js/issues/3682#issuecomment-264348200",
+ "render-tests/line-join/property-function": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
+ "render-tests/line-join/property-function-dasharray": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
+ "render-tests/line-opacity/step-curve": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
"render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
- "render-tests/regressions/mapbox-gl-js#3010": "skip - needs issue",
- "render-tests/regressions/mapbox-gl-js#3548": "skip - needs issue",
"render-tests/regressions/mapbox-gl-js#3682": "https://github.com/mapbox/mapbox-gl-js/issues/3682",
- "render-tests/regressions/mapbox-gl-js#4550": "skip - https://github.com/mapbox/mapbox-gl-native/issues/1350",
- "render-tests/regressions/mapbox-gl-js#4551": "skip - https://github.com/mapbox/mapbox-gl-native/issues/1350",
- "render-tests/regressions/mapbox-gl-js#4573": "skip - https://github.com/mapbox/mapbox-gl-native/issues/1350",
"render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357",
- "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "skip - https://github.com/mapbox/mapbox-gl-native/issues/6745",
- "render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "skip - needs issue",
- "render-tests/runtime-styling/source-add-geojson-inline": "skip - needs issue",
+ "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847",
+ "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
+ "render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
"render-tests/symbol-placement/line": "needs issue",
+ "render-tests/text-font/camera-function": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
"render-tests/text-keep-upright/line-placement-true-offset": "https://github.com/mapbox/mapbox-gl-native/issues/9271",
- "render-tests/text-size/composite-function-high-base": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
- "render-tests/video/default": "skip - needs issue"
+ "render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/auto-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/map-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/viewport-overzoomed": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
+ "render-tests/text-pitch-alignment/viewport-overzoomed-single-glyph": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
+ "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-size/composite-expression": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601",
+ "render-tests/heatmap-color/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-color/expression": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-color/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-intensity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-intensity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-intensity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-opacity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-opacity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-opacity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/antimeridian": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/pitch30": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-weight/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-weight/identity-property-function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-weight/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146"
}
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index 04d02d0558..39665e41ef 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -109,6 +109,7 @@ test('Map', function(t) {
'release',
'cancel',
'addSource',
+ 'removeSource',
'addLayer',
'removeLayer',
'addImage',
diff --git a/platform/node/test/render.test.js b/platform/node/test/render.test.js
index c8a214e919..812a531f20 100644
--- a/platform/node/test/render.test.js
+++ b/platform/node/test/render.test.js
@@ -4,10 +4,4 @@ const suite = require('../../../mapbox-gl-js/test/integration').render;
const suiteImplementation = require('./suite_implementation');
const ignores = require('./ignores.json');
-let tests;
-
-if (process.argv[1] === __filename && process.argv.length > 2) {
- tests = process.argv.slice(2);
-}
-
-suite.run('native', {tests: tests, ignores: ignores}, suiteImplementation);
+suite.run('native', ignores, suiteImplementation);
diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js
index b717ecd2b2..323f429bed 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -10,23 +10,27 @@ mbgl.on('message', function(msg) {
console.log('%s (%s): %s', msg.severity, msg.class, msg.text);
});
+// Map of map objects by pixel ratio
+var maps = new Map();
+
module.exports = function (style, options, callback) {
- var map = new mbgl.Map({
- ratio: options.pixelRatio,
- request: function(req, callback) {
- request(req.url, {encoding: null}, function (err, response, body) {
- if (err) {
- callback(err);
- } else if (response.statusCode == 404) {
- callback();
- } else if (response.statusCode != 200) {
- callback(new Error(response.statusMessage));
- } else {
- callback(null, {data: body});
- }
- });
+ if (options.recycleMap) {
+ if (maps.has(options.pixelRatio)) {
+ var map = maps.get(options.pixelRatio);
+ map.request = mapRequest;
+ } else {
+ maps.set(options.pixelRatio, new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest
+ }));
+ var map = maps.get(options.pixelRatio);
}
- });
+ } else {
+ var map = new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest
+ });
+ }
var timedOut = false;
var watchdog = setTimeout(function () {
@@ -46,14 +50,30 @@ module.exports = function (style, options, callback) {
options.bearing = style.bearing || 0;
options.pitch = style.pitch || 0;
- map.load(style);
+ map.load(style, { defaultStyleCamera: true });
+
+ function mapRequest(req, callback) {
+ request(req.url, {encoding: null}, function (err, response, body) {
+ if (err) {
+ callback(err);
+ } else if (response.statusCode == 404) {
+ callback();
+ } else if (response.statusCode != 200) {
+ callback(new Error(response.statusMessage));
+ } else {
+ callback(null, {data: body});
+ }
+ });
+ };
applyOperations(options.operations, function() {
map.render(options, function (err, pixels) {
var results = options.queryGeometry ?
map.queryRenderedFeatures(options.queryGeometry, options.queryOptions || {}) :
[];
- map.release();
+ if (!options.recycleMap) {
+ map.release();
+ }
if (timedOut) return;
clearTimeout(watchdog);
callback(err, pixels, results.map(prepareFeatures));
@@ -72,12 +92,16 @@ module.exports = function (style, options, callback) {
} else if (operation[0] === 'addImage' || operation[0] === 'updateImage') {
var img = PNG.sync.read(fs.readFileSync(path.join(__dirname, '../../../mapbox-gl-js/test/integration', operation[2])));
+ const testOpts = (operation.length > 3) ? operation[3] : {};
- map.addImage(operation[1], img.data, {
+ const options = {
height: img.height,
width: img.width,
- pixelRatio: operation[3] || 1
- });
+ pixelRatio: testOpts.pixelRatio || 1,
+ sdf: testOpts.sdf || false
+ }
+
+ map.addImage(operation[1], img.data, options);
applyOperations(operations.slice(1), callback);
diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake
index 732fb1de28..a7fdbf3542 100644
--- a/platform/qt/config.cmake
+++ b/platform/qt/config.cmake
@@ -15,7 +15,7 @@ endif()
macro(mbgl_platform_core)
target_sources(mbgl-core
- ${MBGL_QT_FILES}
+ ${MBGL_QT_CORE_FILES}
)
target_include_directories(mbgl-core
@@ -24,7 +24,7 @@ macro(mbgl_platform_core)
)
target_link_libraries(mbgl-core
- ${MBGL_QT_LIBRARIES}
+ ${MBGL_QT_CORE_LIBRARIES}
)
if(NOT WITH_QT_DECODERS)
@@ -50,17 +50,23 @@ macro(mbgl_platform_core)
endmacro()
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ ${MBGL_QT_FILESOURCE_FILES}
+ )
+
+ target_link_libraries(mbgl-filesource
+ ${MBGL_QT_FILESOURCE_LIBRARIES}
+ )
+endmacro()
+
+
macro(mbgl_platform_test)
target_sources(mbgl-test
- PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
- PRIVATE platform/default/mbgl/gl/headless_frontend.hpp
- PRIVATE platform/default/mbgl/gl/headless_backend.cpp
- PRIVATE platform/default/mbgl/gl/headless_backend.hpp
- PRIVATE platform/default/mbgl/gl/headless_display.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.hpp
- PRIVATE platform/qt/test/headless_backend_qt.cpp
PRIVATE platform/qt/test/main.cpp
- PRIVATE platform/qt/test/qmapboxgl.cpp
+ PRIVATE platform/qt/test/qmapboxgl.test.cpp
+ PRIVATE platform/qt/test/qmapboxgl.test.cpp
)
set_source_files_properties(
@@ -72,7 +78,7 @@ macro(mbgl_platform_test)
target_link_libraries(mbgl-test
PRIVATE qmapboxgl
- ${MBGL_QT_TEST_LIBRARIES}
+ PRIVATE mbgl-filesource
)
endmacro()
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
index e2fb283989..ebbd949921 100644
--- a/platform/qt/include/qmapboxgl.hpp
+++ b/platform/qt/include/qmapboxgl.hpp
@@ -10,6 +10,8 @@
#include <QString>
#include <QStringList>
+#include <functional>
+
class QMapboxGLPrivate;
// This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style
@@ -59,6 +61,9 @@ public:
QString apiBaseUrl() const;
void setApiBaseUrl(const QString &);
+ std::function<std::string(const std::string &&)> resourceTransform() const;
+ void setResourceTransform(const std::function<std::string(const std::string &&)> &);
+
private:
GLContextMode m_contextMode;
ConstrainMode m_constrainMode;
@@ -69,6 +74,7 @@ private:
QString m_assetPath;
QString m_accessToken;
QString m_apiBaseUrl;
+ std::function<std::string(const std::string &&)> m_resourceTransform;
};
struct Q_DECL_EXPORT QMapboxGLCameraOptions {
diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake
index 489ae5ed08..2346d7d820 100644
--- a/platform/qt/qt.cmake
+++ b/platform/qt/qt.cmake
@@ -10,22 +10,16 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -D__QT__")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(MBGL_QT_FILES
- # File source
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.hpp
+set(MBGL_QT_CORE_FILES
+ # Headless view
+ PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
+ PRIVATE platform/default/mbgl/gl/headless_frontend.hpp
+ PRIVATE platform/default/mbgl/gl/headless_backend.cpp
+ PRIVATE platform/default/mbgl/gl/headless_backend.hpp
+ PRIVATE platform/default/mbgl/gl/headless_display.cpp
+ PRIVATE platform/default/mbgl/gl/headless_display.hpp
+ PRIVATE platform/qt/src/headless_backend_qt.cpp
# Misc
PRIVATE platform/default/logging_stderr.cpp
@@ -42,22 +36,24 @@ set(MBGL_QT_FILES
# Platform integration
PRIVATE platform/qt/src/async_task.cpp
PRIVATE platform/qt/src/async_task_impl.hpp
- PRIVATE platform/qt/src/http_file_source.cpp
- PRIVATE platform/qt/src/http_file_source.hpp
- PRIVATE platform/qt/src/http_request.cpp
- PRIVATE platform/qt/src/http_request.hpp
PRIVATE platform/qt/src/qt_image.cpp
PRIVATE platform/qt/src/run_loop.cpp
PRIVATE platform/qt/src/run_loop_impl.hpp
- PRIVATE platform/qt/src/sqlite3.cpp
PRIVATE platform/qt/src/string_stdlib.cpp
PRIVATE platform/qt/src/timer.cpp
PRIVATE platform/qt/src/timer_impl.hpp
PRIVATE platform/qt/src/utf.cpp
)
-include_directories(
- PRIVATE platform/qt/include
+set(MBGL_QT_FILESOURCE_FILES
+ # File source
+ PRIVATE platform/qt/src/http_file_source.cpp
+ PRIVATE platform/qt/src/http_file_source.hpp
+ PRIVATE platform/qt/src/http_request.cpp
+ PRIVATE platform/qt/src/http_request.hpp
+
+ # Database
+ PRIVATE platform/qt/src/sqlite3.cpp
)
# Shared library
@@ -72,6 +68,17 @@ add_library(qmapboxgl SHARED
platform/default/mbgl/util/default_styles.hpp
)
+target_include_directories(qmapboxgl
+ PUBLIC platform/qt/include
+)
+
+target_link_libraries(qmapboxgl
+ PRIVATE mbgl-core
+ PRIVATE mbgl-filesource
+ ${MBGL_QT_CORE_LIBRARIES}
+ ${MBGL_QT_FILESOURCE_LIBRARIES}
+)
+
# C++ app
add_executable(mbgl-qt
platform/qt/app/main.cpp
@@ -80,6 +87,20 @@ add_executable(mbgl-qt
platform/qt/resources/common.qrc
)
+target_compile_options(qmapboxgl
+ PRIVATE -std=c++03
+)
+
+target_link_libraries(mbgl-qt
+ PRIVATE qmapboxgl
+)
+
+if(WITH_QT_4)
+ include(platform/qt/qt4.cmake)
+else()
+ include(platform/qt/qt5.cmake)
+endif()
+
xcode_create_scheme(TARGET mbgl-qt)
if(WITH_QT_4)
@@ -90,22 +111,22 @@ endif()
# OS specific configurations
if (MASON_PLATFORM STREQUAL "osx" OR MASON_PLATFORM STREQUAL "ios")
- list(APPEND MBGL_QT_FILES
+ list(APPEND MBGL_QT_CORE_FILES
PRIVATE platform/darwin/src/nsthread.mm
)
- list(APPEND MBGL_QT_LIBRARIES
+ list(APPEND MBGL_QT_CORE_LIBRARIES
PRIVATE "-framework Foundation"
PRIVATE "-framework OpenGL"
)
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
- list(APPEND MBGL_QT_FILES
+ list(APPEND MBGL_QT_CORE_FILES
PRIVATE platform/default/thread.cpp
)
- list(APPEND MBGL_QT_LIBRARIES
+ list(APPEND MBGL_QT_CORE_LIBRARIES
PRIVATE -lGL
)
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
- list(APPEND MBGL_QT_FILES
+ list(APPEND MBGL_QT_CORE_FILES
PRIVATE platform/qt/src/thread.cpp
)
endif()
diff --git a/platform/qt/qt4.cmake b/platform/qt/qt4.cmake
index 66aed87c38..272009c541 100644
--- a/platform/qt/qt4.cmake
+++ b/platform/qt/qt4.cmake
@@ -1,31 +1,20 @@
find_package(Qt4 REQUIRED)
-set(MBGL_QT_LIBRARIES
- PRIVATE Qt4::QtCore
- PRIVATE Qt4::QtGui
- PRIVATE Qt4::QtNetwork
- PRIVATE Qt4::QtOpenGL
- PRIVATE Qt4::QtSql
+set(MBGL_QT_CORE_LIBRARIES
+ PUBLIC Qt4::QtCore
+ PUBLIC Qt4::QtGui
+ PUBLIC Qt4::QtOpenGL
)
-set(MBGL_QT_TEST_LIBRARIES
- PRIVATE Qt4::QtCore
- PRIVATE Qt4::QtOpenGL
+set(MBGL_QT_FILESOURCE_LIBRARIES
+ PUBLIC Qt4::QtNetwork
+ PUBLIC Qt4::QtSql
)
target_compile_options(qmapboxgl
PRIVATE -Wno-inconsistent-missing-override
)
-target_link_libraries(qmapboxgl
- PRIVATE mbgl-core
- PRIVATE Qt4::QtCore
- PRIVATE Qt4::QtGui
- PRIVATE Qt4::QtOpenGL
-)
-
target_link_libraries(mbgl-qt
- PRIVATE qmapboxgl
- PRIVATE Qt4::QtGui
PRIVATE Qt4::QtOpenGL
)
diff --git a/platform/qt/qt5.cmake b/platform/qt/qt5.cmake
index ed51051311..c4af774ba3 100644
--- a/platform/qt/qt5.cmake
+++ b/platform/qt/qt5.cmake
@@ -1,35 +1,21 @@
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
-find_package(Qt5Location REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Sql REQUIRED)
-set(MBGL_QT_LIBRARIES
- PRIVATE Qt5::Core
- PRIVATE Qt5::Gui
- PRIVATE Qt5::Network
- PRIVATE Qt5::Sql
+set(MBGL_QT_CORE_LIBRARIES
+ PUBLIC Qt5::Core
+ PUBLIC Qt5::Gui
+ PUBLIC Qt5::OpenGL
)
-set(MBGL_QT_TEST_LIBRARIES
- PRIVATE Qt5::Core
- PRIVATE Qt5::Gui
- PRIVATE Qt5::Widgets
- PRIVATE Qt5::OpenGL
-)
-
-target_link_libraries(qmapboxgl
- PRIVATE mbgl-core
- PRIVATE Qt5::Core
- PRIVATE Qt5::Gui
- PRIVATE Qt5::Location
- PRIVATE Qt5::Sql
+set(MBGL_QT_FILESOURCE_LIBRARIES
+ PUBLIC Qt5::Network
+ PUBLIC Qt5::Sql
)
target_link_libraries(mbgl-qt
- PRIVATE qmapboxgl
- PRIVATE Qt5::OpenGL
PRIVATE Qt5::Widgets
)
diff --git a/platform/qt/test/headless_backend_qt.cpp b/platform/qt/src/headless_backend_qt.cpp
index 5f95b2f96a..5f95b2f96a 100644
--- a/platform/qt/test/headless_backend_qt.cpp
+++ b/platform/qt/src/headless_backend_qt.cpp
diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp
index ef753c3e0e..386a2d9ef4 100644
--- a/platform/qt/src/http_request.cpp
+++ b/platform/qt/src/http_request.cpp
@@ -82,7 +82,9 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply)
} else if (header == "etag") {
response.etag = std::string(line.second.constData(), line.second.size());
} else if (header == "cache-control") {
- response.expires = http::CacheControl::parse(line.second.constData()).toTimePoint();
+ const auto cc = http::CacheControl::parse(line.second.constData());
+ response.expires = cc.toTimePoint();
+ response.mustRevalidate = cc.mustRevalidate;
} else if (header == "expires") {
response.expires = util::parseTimestamp(line.second.constData());
} else if (header == "retry-after") {
diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp
index eeb0bece12..074ef280aa 100644
--- a/platform/qt/src/qmapboxgl.cpp
+++ b/platform/qt/src/qmapboxgl.cpp
@@ -8,6 +8,7 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/math/log2.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/conversion.hpp>
@@ -28,6 +29,7 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/shared_thread_pool.hpp>
#include <mbgl/util/traits.hpp>
+#include <mbgl/actor/scheduler.hpp>
#if QT_VERSION >= 0x050000
#include <QGuiApplication>
@@ -361,6 +363,27 @@ void QMapboxGLSettings::setApiBaseUrl(const QString& url)
}
/*!
+ Returns resource transformation callback used to transform requested URLs.
+*/
+std::function<std::string(const std::string &&)> QMapboxGLSettings::resourceTransform() const
+{
+ return m_resourceTransform;
+}
+
+/*!
+ Sets the resource transformation callback.
+
+ When given, resource transformation callback will be used to transform the
+ requested resource URLs before they are requested from internet. This can be
+ used add or remove custom parameters, or reroute certain requests to other
+ servers or endpoints.
+*/
+void QMapboxGLSettings::setResourceTransform(const std::function<std::string(const std::string &&)> &transform)
+{
+ m_resourceTransform = transform;
+}
+
+/*!
\class QMapboxGL
\brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine.
@@ -583,7 +606,7 @@ double QMapboxGL::scale() const
void QMapboxGL::setScale(double scale_, const QPointF &center)
{
- d_ptr->mapObj->setZoom(std::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
+ d_ptr->mapObj->setZoom(mbgl::util::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
}
/*!
@@ -991,7 +1014,7 @@ void QMapboxGL::moveBy(const QPointF &offset)
can be used for implementing a pinch gesture.
*/
void QMapboxGL::scaleBy(double scale_, const QPointF &center) {
- d_ptr->mapObj->setZoom(d_ptr->mapObj->getZoom() + std::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
+ d_ptr->mapObj->setZoom(d_ptr->mapObj->getZoom() + mbgl::util::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
}
/*!
@@ -1498,13 +1521,25 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin
settings.cacheDatabaseMaximumSize()))
, threadPool(mbgl::sharedThreadPool())
{
+ // Setup resource transform if needed
+ if (settings.resourceTransform()) {
+ m_resourceTransform =
+ std::make_unique< mbgl::Actor<mbgl::ResourceTransform> >( *mbgl::Scheduler::GetCurrent(),
+ [callback = settings.resourceTransform()]
+ (mbgl::Resource::Kind , const std::string&& url_) -> std::string {
+ return callback(std::move(url_));
+ }
+ );
+ fileSourceObj->setResourceTransform(m_resourceTransform->self());
+ }
+
// Setup and connect the renderer frontend
frontend = std::make_unique<QMapboxGLRendererFrontend>(
std::make_unique<mbgl::Renderer>(*this, pixelRatio, *fileSourceObj, *threadPool,
static_cast<mbgl::GLContextMode>(settings.contextMode())),
*this);
connect(frontend.get(), SIGNAL(updated()), this, SLOT(invalidate()));
-
+
mapObj = std::make_unique<mbgl::Map>(
*frontend,
*this, sanitizedSize(size),
@@ -1527,18 +1562,18 @@ QMapboxGLPrivate::~QMapboxGLPrivate()
{
}
-mbgl::Size QMapboxGLPrivate::framebufferSize() const {
+mbgl::Size QMapboxGLPrivate::getFramebufferSize() const {
return sanitizedSize(fbSize);
}
void QMapboxGLPrivate::updateAssumedState() {
assumeFramebufferBinding(fbObject);
- assumeViewport(0, 0, framebufferSize());
+ assumeViewport(0, 0, getFramebufferSize());
}
void QMapboxGLPrivate::bind() {
setFramebufferBinding(fbObject);
- setViewport(0, 0, framebufferSize());
+ setViewport(0, 0, getFramebufferSize());
}
void QMapboxGLPrivate::invalidate()
diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp
index 7b0dd8c192..5e12b44a20 100644
--- a/platform/qt/src/qmapboxgl_p.hpp
+++ b/platform/qt/src/qmapboxgl_p.hpp
@@ -3,15 +3,19 @@
#include "qmapboxgl.hpp"
#include "qmapboxgl_renderer_frontend_p.hpp"
+#include <mbgl/actor/actor.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/renderer/renderer_backend.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/geo.hpp>
+#include <mbgl/storage/resource_transform.hpp>
#include <QObject>
#include <QSize>
+#include <memory>
+
class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver
{
Q_OBJECT
@@ -20,10 +24,10 @@ public:
explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio);
virtual ~QMapboxGLPrivate();
- mbgl::Size framebufferSize() const;
// mbgl::RendererBackend implementation.
void bind() final;
+ mbgl::Size getFramebufferSize() const final;
void updateAssumedState() final;
void activate() final {}
void deactivate() final {}
@@ -68,4 +72,7 @@ signals:
void needsRendering();
void mapChanged(QMapboxGL::MapChange);
void copyrightsChanged(const QString &copyrightsHtml);
+
+private:
+ std::unique_ptr< mbgl::Actor<mbgl::ResourceTransform> > m_resourceTransform;
};
diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp
index c44f284852..71ea19032a 100644
--- a/platform/qt/src/run_loop.cpp
+++ b/platform/qt/src/run_loop.cpp
@@ -1,6 +1,6 @@
#include "run_loop_impl.hpp"
-#include <mbgl/util/thread_local.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <QCoreApplication>
@@ -8,13 +8,6 @@
#include <functional>
#include <utility>
-namespace {
-
-using namespace mbgl::util;
-static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
-
-}
-
namespace mbgl {
namespace util {
@@ -27,7 +20,8 @@ void RunLoop::Impl::onWriteEvent(int fd) {
}
RunLoop* RunLoop::Get() {
- return current.get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
@@ -42,14 +36,14 @@ RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
impl->type = type;
- current.set(this);
+ Scheduler::SetCurrent(this);
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
}
RunLoop::~RunLoop() {
MBGL_VERIFY_THREAD(tid);
- current.set(nullptr);
+ Scheduler::SetCurrent(nullptr);
}
LOOP_HANDLE RunLoop::getLoopHandle() {
diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp
index 0cd78d85ce..7d47ae552b 100644
--- a/platform/qt/src/sqlite3.cpp
+++ b/platform/qt/src/sqlite3.cpp
@@ -312,6 +312,7 @@ bool Statement::run() {
return impl->query.next();
}
+template bool Statement::get(int);
template int Statement::get(int);
template int64_t Statement::get(int);
template double Statement::get(int);
diff --git a/platform/qt/src/thread_local.cpp b/platform/qt/src/thread_local.cpp
index bf2103c98f..467bfb0d05 100644
--- a/platform/qt/src/thread_local.cpp
+++ b/platform/qt/src/thread_local.cpp
@@ -1,6 +1,6 @@
#include <mbgl/util/thread_local.hpp>
-#include <mbgl/util/run_loop.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <mbgl/renderer/backend_scope.hpp>
#include <array>
@@ -41,7 +41,7 @@ void ThreadLocal<T>::set(T* ptr) {
impl->local.localData()[0] = ptr;
}
-template class ThreadLocal<RunLoop>;
+template class ThreadLocal<Scheduler>;
template class ThreadLocal<BackendScope>;
template class ThreadLocal<int>; // For unit tests
diff --git a/platform/qt/test/qmapboxgl.cpp b/platform/qt/test/qmapboxgl.cpp
deleted file mode 100644
index 747f8796fa..0000000000
--- a/platform/qt/test/qmapboxgl.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/util/io.hpp>
-
-#include <QApplication>
-#include <QMapbox>
-#include <QMapboxGL>
-
-// We're using QGLFramebufferObject, which is only available in Qt 5 and up.
-#if QT_VERSION >= 0x050000
-
-#include <QGLWidget>
-#include <QGLFramebufferObject>
-
-class QMapboxGLTest : public QObject, public ::testing::Test {
- Q_OBJECT
-
-public:
- QMapboxGLTest() : size(512, 512), fbo((assert(widget.context()->isValid()), widget.makeCurrent(), size)), map(nullptr, settings, size) {
- connect(&map, SIGNAL(mapChanged(QMapboxGL::MapChange)),
- this, SLOT(onMapChanged(QMapboxGL::MapChange)));
- connect(&map, SIGNAL(needsRendering()),
- this, SLOT(onNeedsRendering()));
- map.resize(fbo.size(), fbo.size());
- map.setFramebufferObject(fbo.handle());
- map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14);
- }
-
- void runUntil(QMapboxGL::MapChange status) {
- changeCallback = [&](QMapboxGL::MapChange change) {
- if (change == status) {
- qApp->exit();
- changeCallback = nullptr;
- }
- };
-
- qApp->exec();
- }
-
-private:
- QGLWidget widget;
- const QSize size;
- QGLFramebufferObject fbo;
-
-protected:
- QMapboxGLSettings settings;
- QMapboxGL map;
-
- std::function<void(QMapboxGL::MapChange)> changeCallback;
-
-private slots:
- void onMapChanged(QMapboxGL::MapChange change) {
- if (changeCallback) {
- changeCallback(change);
- }
- };
-
- void onNeedsRendering() {
- widget.makeCurrent();
- fbo.bind();
- glViewport(0, 0, fbo.width(), fbo.height());
- map.render();
- };
-};
-
-TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleJson)) {
- QString json = QString::fromStdString(
- mbgl::util::read_file("test/fixtures/resources/style_vector.json"));
-
- map.setStyleJson(json);
- ASSERT_EQ(map.styleJson(), json);
- runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
-
- map.setStyleJson("invalid json");
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-
- map.setStyleJson("\"\"");
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-
- map.setStyleJson(QString());
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-}
-
-TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleUrl)) {
- QString url(QMapbox::defaultStyles()[0].first);
-
- map.setStyleUrl(url);
- ASSERT_EQ(map.styleUrl(), url);
- runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
-
- map.setStyleUrl("invalid://url");
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-
- map.setStyleUrl(QString());
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-}
-
-#include "qmapboxgl.moc"
-
-#endif
diff --git a/platform/qt/test/qmapboxgl.test.cpp b/platform/qt/test/qmapboxgl.test.cpp
new file mode 100644
index 0000000000..c6ae3ed403
--- /dev/null
+++ b/platform/qt/test/qmapboxgl.test.cpp
@@ -0,0 +1,77 @@
+#include "qmapboxgl.test.hpp"
+
+#include <mbgl/util/io.hpp>
+
+#include <QMapbox>
+
+// We're using QGLFramebufferObject, which is only available in Qt 5 and up.
+#if QT_VERSION >= 0x050000
+
+QMapboxGLTest::QMapboxGLTest() : size(512, 512), fbo((assert(widget.context()->isValid()), widget.makeCurrent(), size)), map(nullptr, settings, size) {
+ connect(&map, SIGNAL(mapChanged(QMapboxGL::MapChange)),
+ this, SLOT(onMapChanged(QMapboxGL::MapChange)));
+ connect(&map, SIGNAL(needsRendering()),
+ this, SLOT(onNeedsRendering()));
+ map.resize(fbo.size(), fbo.size());
+ map.setFramebufferObject(fbo.handle());
+ map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14);
+}
+
+void QMapboxGLTest::runUntil(QMapboxGL::MapChange status) {
+ changeCallback = [&](QMapboxGL::MapChange change) {
+ if (change == status) {
+ qApp->exit();
+ changeCallback = nullptr;
+ }
+ };
+
+ qApp->exec();
+}
+
+void QMapboxGLTest::onMapChanged(QMapboxGL::MapChange change) {
+ if (changeCallback) {
+ changeCallback(change);
+ }
+}
+
+void QMapboxGLTest::onNeedsRendering() {
+ widget.makeCurrent();
+ fbo.bind();
+ glViewport(0, 0, fbo.width(), fbo.height());
+ map.render();
+}
+
+
+TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleJson)) {
+ QString json = QString::fromStdString(
+ mbgl::util::read_file("test/fixtures/resources/style_vector.json"));
+
+ map.setStyleJson(json);
+ ASSERT_EQ(map.styleJson(), json);
+ runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
+
+ map.setStyleJson("invalid json");
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+
+ map.setStyleJson("\"\"");
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+
+ map.setStyleJson(QString());
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+}
+
+TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleUrl)) {
+ QString url(QMapbox::defaultStyles()[0].first);
+
+ map.setStyleUrl(url);
+ ASSERT_EQ(map.styleUrl(), url);
+ runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
+
+ map.setStyleUrl("invalid://url");
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+
+ map.setStyleUrl(QString());
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+}
+
+#endif
diff --git a/platform/qt/test/qmapboxgl.test.hpp b/platform/qt/test/qmapboxgl.test.hpp
new file mode 100644
index 0000000000..04d63610ca
--- /dev/null
+++ b/platform/qt/test/qmapboxgl.test.hpp
@@ -0,0 +1,36 @@
+#include <mbgl/test/util.hpp>
+
+#include <QApplication>
+#include <QMapboxGL>
+
+// We're using QGLFramebufferObject, which is only available in Qt 5 and up.
+#if QT_VERSION >= 0x050000
+
+#include <QGLWidget>
+#include <QGLFramebufferObject>
+
+class QMapboxGLTest : public QObject, public ::testing::Test {
+ Q_OBJECT
+
+public:
+ QMapboxGLTest();
+
+ void runUntil(QMapboxGL::MapChange);
+
+private:
+ QGLWidget widget;
+ const QSize size;
+ QGLFramebufferObject fbo;
+
+protected:
+ QMapboxGLSettings settings;
+ QMapboxGL map;
+
+ std::function<void(QMapboxGL::MapChange)> changeCallback;
+
+private slots:
+ void onMapChanged(QMapboxGL::MapChange);
+ void onNeedsRendering();
+};
+
+#endif
diff --git a/scripts/config.xcconfig.in b/scripts/config.xcconfig.in
index eb6bc71b89..357732c9ae 100644
--- a/scripts/config.xcconfig.in
+++ b/scripts/config.xcconfig.in
@@ -3,3 +3,7 @@
// mbgl-core
mbgl_core_INCLUDE_DIRECTORIES = "@mbgl_core_INCLUDE_DIRECTORIES@"
mbgl_core_LINK_LIBRARIES = "@mbgl_core_LINK_LIBRARIES@"
+
+// mbgl-filesource
+mbgl_filesource_INCLUDE_DIRECTORIES = "@mbgl_filesource_INCLUDE_DIRECTORIES@"
+mbgl_filesource_LINK_LIBRARIES = "@mbgl_filesource_LINK_LIBRARIES@"
diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js
index a10e505278..cf54b1b100 100755
--- a/scripts/generate-shaders.js
+++ b/scripts/generate-shaders.js
@@ -1,16 +1,13 @@
#!/usr/bin/env node
-const path = require('path');
-const fs = require('fs');
+require('flow-remove-types/register');
-const inputPath = 'mapbox-gl-js/src/shaders';
+const path = require('path');
+const shaders = require('../mapbox-gl-js/src/shaders');
const outputPath = 'src/mbgl/shaders';
require('./style-code');
-const vertexPrelude = fs.readFileSync(path.join(inputPath, '_prelude.vertex.glsl'));
-const fragmentPrelude = fs.readFileSync(path.join(inputPath, '_prelude.fragment.glsl'));
-
writeIfModified(path.join(outputPath, 'preludes.hpp'), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
#pragma once
@@ -33,84 +30,21 @@ namespace mbgl {
namespace shaders {
const char* vertexPrelude = R"MBGL_SHADER(
-${vertexPrelude}
+${shaders.prelude.vertexSource}
)MBGL_SHADER";
const char* fragmentPrelude = R"MBGL_SHADER(
-${fragmentPrelude}
+${shaders.prelude.fragmentSource}
)MBGL_SHADER";
} // namespace shaders
} // namespace mbgl
`);
-[
- 'circle',
- 'collision_box',
- 'debug',
- 'extrusion_texture',
- 'fill',
- 'fill_extrusion',
- 'fill_extrusion_pattern',
- 'fill_outline',
- 'fill_outline_pattern',
- 'fill_pattern',
- 'line',
- 'line_pattern',
- 'line_sdf',
- 'raster',
- 'symbol_icon',
- 'symbol_sdf'
-].forEach(function (shaderName) {
- const re = / *#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g;
- const fragmentPragmas = new Set();
-
- let fragmentSource = fs.readFileSync(path.join(inputPath, shaderName + '.fragment.glsl'), 'utf8');
- let vertexSource = fs.readFileSync(path.join(inputPath, shaderName + '.vertex.glsl'), 'utf8');
-
- fragmentSource = fragmentSource.replace(re, (match, operation, precision, type, name) => {
- fragmentPragmas.add(name);
- return operation === "define" ? `
-#ifndef HAS_UNIFORM_u_${name}
-varying ${precision} ${type} ${name};
-#else
-uniform ${precision} ${type} u_${name};
-#endif` : `
-#ifdef HAS_UNIFORM_u_${name}
- ${precision} ${type} ${name} = u_${name};
-#endif`;
- });
-
- vertexSource = vertexSource.replace(re, (match, operation, precision, type, name) => {
- const a_type = type === "float" ? "vec2" : "vec4";
- if (fragmentPragmas.has(name)) {
- return operation === "define" ? `
-#ifndef HAS_UNIFORM_u_${name}
-uniform lowp float a_${name}_t;
-attribute ${precision} ${a_type} a_${name};
-varying ${precision} ${type} ${name};
-#else
-uniform ${precision} ${type} u_${name};
-#endif` : `
-#ifndef HAS_UNIFORM_u_${name}
- ${name} = unpack_mix_${a_type}(a_${name}, a_${name}_t);
-#else
- ${precision} ${type} ${name} = u_${name};
-#endif`;
- } else {
- return operation === "define" ? `
-#ifndef HAS_UNIFORM_u_${name}
-uniform lowp float a_${name}_t;
-attribute ${precision} ${a_type} a_${name};
-#else
-uniform ${precision} ${type} u_${name};
-#endif` : `
-#ifndef HAS_UNIFORM_u_${name}
- ${precision} ${type} ${name} = unpack_mix_${a_type}(a_${name}, a_${name}_t);
-#else
- ${precision} ${type} ${name} = u_${name};
-#endif`;
- }
- });
+for (const key in shaders) {
+ if (key === 'prelude')
+ continue;
+
+ const shaderName = key.replace(/[A-Z]+/g, (match) => `_${match.toLowerCase()}`);
writeIfModified(path.join(outputPath, `${shaderName}.hpp`), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
@@ -139,13 +73,13 @@ namespace shaders {
const char* ${shaderName}::name = "${shaderName}";
const char* ${shaderName}::vertexSource = R"MBGL_SHADER(
-${vertexSource}
+${shaders[key].vertexSource}
)MBGL_SHADER";
const char* ${shaderName}::fragmentSource = R"MBGL_SHADER(
-${fragmentSource}
+${shaders[key].fragmentSource}
)MBGL_SHADER";
} // namespace shaders
} // namespace mbgl
`);
-});
+}
diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js
index b1d0ed28ec..fe9a1a906b 100644
--- a/scripts/generate-style-code.js
+++ b/scripts/generate-style-code.js
@@ -29,6 +29,9 @@ global.evaluatedType = function (property) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
+ if (/^(text|icon)-anchor$/.test(property.name)) {
+ return 'SymbolAnchorType';
+ }
if (/position/.test(property.name)) {
return 'Position';
}
diff --git a/src/mbgl/actor/scheduler.cpp b/src/mbgl/actor/scheduler.cpp
new file mode 100644
index 0000000000..d7cdb2737b
--- /dev/null
+++ b/src/mbgl/actor/scheduler.cpp
@@ -0,0 +1,19 @@
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/util/thread_local.hpp>
+
+namespace mbgl {
+
+static auto& current() {
+ static util::ThreadLocal<Scheduler> scheduler;
+ return scheduler;
+};
+
+void Scheduler::SetCurrent(Scheduler* scheduler) {
+ current().set(scheduler);
+}
+
+Scheduler* Scheduler::GetCurrent() {
+ return current().get();
+}
+
+} //namespace mbgl
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp
index dbf8387ae0..1f2d01e9eb 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -39,19 +39,22 @@ AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, cons
Annotation::visit(annotation, [&] (const auto& annotation_) {
this->add(id, annotation_, maxZoom);
});
+ dirty = true;
return id;
}
-Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
+bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
std::lock_guard<std::mutex> lock(mutex);
- return Annotation::visit(annotation, [&] (const auto& annotation_) {
- return this->update(id, annotation_, maxZoom);
+ Annotation::visit(annotation, [&] (const auto& annotation_) {
+ this->update(id, annotation_, maxZoom);
});
+ return dirty;
}
void AnnotationManager::removeAnnotation(const AnnotationID& id) {
std::lock_guard<std::mutex> lock(mutex);
remove(id);
+ dirty = true;
}
void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) {
@@ -72,49 +75,45 @@ void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annota
impl.updateStyle(*style.get().impl);
}
-Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) {
- Update result = Update::Nothing;
-
+void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) {
auto it = symbolAnnotations.find(id);
if (it == symbolAnnotations.end()) {
assert(false); // Attempt to update a non-existent symbol annotation
- return result;
+ return;
}
const SymbolAnnotation& existing = it->second->annotation;
if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) {
- result |= Update::AnnotationData;
+ dirty = true;
remove(id);
add(id, annotation, maxZoom);
}
-
- return result;
}
-Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
auto it = shapeAnnotations.find(id);
if (it == shapeAnnotations.end()) {
assert(false); // Attempt to update a non-existent shape annotation
- return Update::Nothing;
+ return;
}
shapeAnnotations.erase(it);
add(id, annotation, maxZoom);
- return Update::AnnotationData;
+ dirty = true;
}
-Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
auto it = shapeAnnotations.find(id);
if (it == shapeAnnotations.end()) {
assert(false); // Attempt to update a non-existent shape annotation
- return Update::Nothing;
+ return;
}
shapeAnnotations.erase(it);
add(id, annotation, maxZoom);
- return Update::AnnotationData;
+ dirty = true;
}
void AnnotationManager::remove(const AnnotationID& id) {
@@ -187,8 +186,11 @@ void AnnotationManager::updateStyle() {
void AnnotationManager::updateData() {
std::lock_guard<std::mutex> lock(mutex);
- for (auto& tile : tiles) {
- tile->setData(getTileData(tile->id.canonical));
+ if (dirty) {
+ for (auto& tile : tiles) {
+ tile->setData(getTileData(tile->id.canonical));
+ }
+ dirty = false;
}
}
diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp
index dee823bc0f..a028a8f1ba 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -3,7 +3,6 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/style/image.hpp>
-#include <mbgl/map/update.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mutex>
@@ -30,7 +29,7 @@ public:
~AnnotationManager();
AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom);
- Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
+ bool updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
void removeAnnotation(const AnnotationID&);
void addImage(std::unique_ptr<style::Image>);
@@ -53,9 +52,9 @@ private:
void add(const AnnotationID&, const LineAnnotation&, const uint8_t);
void add(const AnnotationID&, const FillAnnotation&, const uint8_t);
- Update update(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
- Update update(const AnnotationID&, const LineAnnotation&, const uint8_t);
- Update update(const AnnotationID&, const FillAnnotation&, const uint8_t);
+ void update(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
+ void update(const AnnotationID&, const LineAnnotation&, const uint8_t);
+ void update(const AnnotationID&, const FillAnnotation&, const uint8_t);
void remove(const AnnotationID&);
@@ -67,6 +66,8 @@ private:
std::mutex mutex;
+ bool dirty = false;
+
AnnotationID nextID = 0;
using SymbolAnnotationTree = boost::geometry::index::rtree<std::shared_ptr<const SymbolAnnotationImpl>, boost::geometry::index::rstar<16, 4>>;
diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp
index de38b596d5..34fb576727 100644
--- a/src/mbgl/annotation/render_annotation_source.cpp
+++ b/src/mbgl/annotation/render_annotation_source.cpp
@@ -38,7 +38,7 @@ void RenderAnnotationSource::update(Immutable<style::Source::Impl> baseImpl_,
parameters,
SourceType::Annotations,
util::tileSize,
- { 0, 22 },
+ { 0, util::DEFAULT_MAX_ZOOM },
[&] (const OverscaledTileID& tileID) {
return std::make_unique<AnnotationTile>(tileID, parameters);
});
@@ -60,9 +60,9 @@ std::vector<std::reference_wrapper<RenderTile>> RenderAnnotationSource::getRende
std::unordered_map<std::string, std::vector<Feature>>
RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
}
std::vector<Feature> RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const {
diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp
index 4000c4b04a..9536b2e101 100644
--- a/src/mbgl/annotation/render_annotation_source.hpp
+++ b/src/mbgl/annotation/render_annotation_source.hpp
@@ -26,7 +26,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp
index 4c4e985369..1adb933e44 100644
--- a/src/mbgl/geometry/feature_index.cpp
+++ b/src/mbgl/geometry/feature_index.cpp
@@ -1,5 +1,4 @@
#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/renderer/render_style.hpp>
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
@@ -9,7 +8,7 @@
#include <mbgl/math/minmax.hpp>
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mapbox/geometry/envelope.hpp>
@@ -32,19 +31,6 @@ void FeatureIndex::insert(const GeometryCollection& geometries,
}
}
-static bool vectorContains(const std::vector<std::string>& vector, const std::string& s) {
- return std::find(vector.begin(), vector.end(), s) != vector.end();
-}
-
-static bool vectorsIntersect(const std::vector<std::string>& vectorA, const std::vector<std::string>& vectorB) {
- for (const auto& a : vectorA) {
- if (vectorContains(vectorB, a)) {
- return true;
- }
- }
- return false;
-}
-
static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) {
return a.sortIndex > b.sortIndex;
}
@@ -53,36 +39,6 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature&
return a.sortIndex < b.sortIndex;
}
-static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions,
- const RenderStyle& style,
- const GeometryTile& tile,
- const float pixelsToTileUnits) {
-
- // Determine the additional radius needed factoring in property functions
- float additionalRadius = 0;
- auto getQueryRadius = [&](const RenderLayer& layer) {
- auto bucket = tile.getBucket(*layer.baseImpl);
- if (bucket) {
- additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits);
- }
- };
-
- if (queryOptions.layerIDs) {
- for (const auto& layerID : *queryOptions.layerIDs) {
- const RenderLayer* layer = style.getRenderLayer(layerID);
- if (layer) {
- getQueryRadius(*layer);
- }
- }
- } else {
- for (const RenderLayer* layer : style.getRenderLayers()) {
- getQueryRadius(*layer);
- }
- }
-
- return std::min<int16_t>(util::EXTENT, additionalRadius);
-}
-
void FeatureIndex::query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
@@ -92,13 +48,13 @@ void FeatureIndex::query(
const RenderedQueryOptions& queryOptions,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const CollisionTile* collisionTile,
- const GeometryTile& tile) const {
+ const float additionalQueryRadius) const {
// Determine query radius
const float pixelsToTileUnits = util::EXTENT / tileSize / scale;
- const int16_t additionalRadius = getAdditionalQueryRadius(queryOptions, style, tile, pixelsToTileUnits);
+ const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits);
// Query the grid index
mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry);
@@ -113,7 +69,7 @@ void FeatureIndex::query(
if (indexedFeature.sortIndex == previousSortIndex) continue;
previousSortIndex = indexedFeature.sortIndex;
- addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits);
}
// Query symbol features, if they've been placed.
@@ -124,7 +80,7 @@ void FeatureIndex::query(
std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale);
std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
for (const auto& symbolFeature : symbolFeatures) {
- addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits);
}
}
@@ -135,30 +91,39 @@ void FeatureIndex::addFeature(
const RenderedQueryOptions& options,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const float bearing,
const float pixelsToTileUnits) const {
- auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName);
- if (options.layerIDs && !vectorsIntersect(layerIDs, *options.layerIDs)) {
- return;
- }
-
- auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
- assert(sourceLayer);
+ auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* {
+ for (const auto& layer : layers) {
+ if (layer->getID() == layerID) {
+ return layer;
+ }
+ }
+ return nullptr;
+ };
- auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
- assert(geometryTileFeature);
+ // Lazily calculated.
+ std::unique_ptr<GeometryTileLayer> sourceLayer;
+ std::unique_ptr<GeometryTileFeature> geometryTileFeature;
- for (const auto& layerID : layerIDs) {
- if (options.layerIDs && !vectorContains(*options.layerIDs, layerID)) {
+ for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) {
+ const RenderLayer* renderLayer = getRenderLayer(layerID);
+ if (!renderLayer) {
continue;
}
- auto renderLayer = style.getRenderLayer(layerID);
- if (!renderLayer ||
- (!renderLayer->is<RenderSymbolLayer>() &&
- !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits))) {
+ if (!geometryTileFeature) {
+ sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
+ assert(sourceLayer);
+
+ geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
+ assert(geometryTileFeature);
+ }
+
+ if (!renderLayer->is<RenderSymbolLayer>() &&
+ !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) {
continue;
}
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index 83f339a9de..2ae7da33df 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -11,9 +11,8 @@
namespace mbgl {
-class GeometryTile;
class RenderedQueryOptions;
-class RenderStyle;
+class RenderLayer;
class CollisionTile;
class CanonicalTileID;
@@ -42,9 +41,9 @@ public:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const CollisionTile*,
- const GeometryTile& tile) const;
+ const float additionalQueryRadius) const;
static optional<GeometryCoordinates> translateQueryGeometry(
const GeometryCoordinates& queryGeometry,
@@ -63,7 +62,7 @@ private:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const float bearing,
const float pixelsToTileUnits) const;
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index e18f1e0bcf..d0c538efb0 100644
--- a/src/mbgl/gl/context.cpp
+++ b/src/mbgl/gl/context.cpp
@@ -87,7 +87,9 @@ static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mis
Context::Context() = default;
Context::~Context() {
- reset();
+ if (cleanupOnDestruction) {
+ reset();
+ }
}
void Context::initializeExtensions(const std::function<gl::ProcAddress(const char*)>& getProcAddress) {
@@ -457,6 +459,9 @@ Framebuffer Context::createFramebuffer(const Texture& color) {
Framebuffer
Context::createFramebuffer(const Texture& color,
const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) {
+ if (color.size != depthTarget.size) {
+ throw std::runtime_error("Renderbuffer size mismatch");
+ }
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0));
@@ -481,7 +486,7 @@ Context::createTexture(const Size size, const void* data, TextureFormat format,
void Context::updateTexture(
TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) {
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = id;
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLenum>(format), size.width,
size.height, 0, static_cast<GLenum>(format), GL_UNSIGNED_BYTE,
@@ -495,7 +500,7 @@ void Context::bindTexture(Texture& obj,
TextureWrap wrapX,
TextureWrap wrapY) {
if (filter != obj.filter || mipmap != obj.mipmap || wrapX != obj.wrapX || wrapY != obj.wrapY) {
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = obj.texture;
if (filter != obj.filter || mipmap != obj.mipmap) {
@@ -526,7 +531,7 @@ void Context::bindTexture(Texture& obj,
} else if (texture[unit] != obj.texture) {
// We are checking first to avoid setting the active texture without a subsequent
// texture bind.
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = obj.texture;
}
}
@@ -558,7 +563,7 @@ void Context::setDirtyState() {
clearStencil.setDirty();
program.setDirty();
lineWidth.setDirty();
- activeTexture.setDirty();
+ activeTextureUnit.setDirty();
pixelStorePack.setDirty();
pixelStoreUnpack.setDirty();
#if not MBGL_USE_GLES2
@@ -709,8 +714,10 @@ void Context::performCleanup() {
if (!abandonedTextures.empty()) {
for (const auto id : abandonedTextures) {
- if (activeTexture == id) {
- activeTexture.setDirty();
+ for (auto& binding : texture) {
+ if (binding == id) {
+ binding.setDirty();
+ }
}
}
MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 9923567276..528113cbba 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -190,7 +190,13 @@ public:
return vertexArray.get();
}
+ void setCleanupOnDestruction(bool cleanup) {
+ cleanupOnDestruction = cleanup;
+ }
+
private:
+ bool cleanupOnDestruction = true;
+
std::unique_ptr<extension::Debugging> debugging;
std::unique_ptr<extension::VertexArray> vertexArray;
#if MBGL_HAS_BINARY_PROGRAMS
@@ -198,7 +204,7 @@ private:
#endif
public:
- State<value::ActiveTexture> activeTexture;
+ State<value::ActiveTextureUnit> activeTextureUnit;
State<value::BindFramebuffer> bindFramebuffer;
State<value::Viewport> viewport;
State<value::ScissorTest> scissorTest;
diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp
index cc8ff13268..0592557a7f 100644
--- a/src/mbgl/gl/renderbuffer.hpp
+++ b/src/mbgl/gl/renderbuffer.hpp
@@ -9,9 +9,23 @@ namespace gl {
template <RenderbufferType renderbufferType>
class Renderbuffer {
public:
+ Renderbuffer(Size size_, UniqueRenderbuffer renderbuffer_, bool dirty_ = false)
+ : size(std::move(size_)), renderbuffer(std::move(renderbuffer_)), dirty(dirty_) {
+ }
+
using type = std::integral_constant<RenderbufferType, renderbufferType>;
Size size;
- gl::UniqueRenderbuffer renderbuffer;
+ UniqueRenderbuffer renderbuffer;
+
+ void shouldClear(bool clear) {
+ dirty = clear;
+ }
+ bool needsClearing() {
+ return dirty;
+ }
+
+private:
+ bool dirty;
};
} // namespace gl
diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp
index 89014fe6bc..092403af0d 100644
--- a/src/mbgl/gl/value.cpp
+++ b/src/mbgl/gl/value.cpp
@@ -242,13 +242,13 @@ LineWidth::Type LineWidth::Get() {
return lineWidth;
}
-const constexpr ActiveTexture::Type ActiveTexture::Default;
+const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default;
-void ActiveTexture::Set(const Type& value) {
+void ActiveTextureUnit::Set(const Type& value) {
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value));
}
-ActiveTexture::Type ActiveTexture::Get() {
+ActiveTextureUnit::Type ActiveTextureUnit::Get() {
GLint activeTexture;
MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture));
return static_cast<Type>(activeTexture - GL_TEXTURE0);
diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp
index 19e9af194f..7b85a5ff4b 100644
--- a/src/mbgl/gl/value.hpp
+++ b/src/mbgl/gl/value.hpp
@@ -165,7 +165,7 @@ struct LineWidth {
static Type Get();
};
-struct ActiveTexture {
+struct ActiveTextureUnit {
using Type = TextureUnit;
static const constexpr Type Default = 0;
static void Set(const Type&);
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 229b8f2ee2..2c90b69b08 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -205,11 +205,11 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
const Shaping result = getShaping(
/* string */ text,
/* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ?
- layout.get<TextMaxWidth>() * oneEm : 0,
+ layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0,
/* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
/* anchor */ layout.evaluate<TextAnchor>(zoom, feature),
/* justify */ layout.evaluate<TextJustify>(zoom, feature),
- /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get<TextLetterSpacing>() * oneEm : 0.0f,
+ /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f,
/* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
/* verticalHeight */ oneEm,
/* writingMode */ writingMode,
@@ -233,6 +233,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
shapedIcon = PositionedIcon::shapeIcon(
imagePositions.at(*feature.icon),
layout.evaluate<IconOffset>(zoom, feature),
+ layout.evaluate<IconAnchor>(zoom, feature),
layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD);
if (image->second->sdf) {
sdfIcons = true;
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
index 8ccd6a0410..279d251f8f 100644
--- a/src/mbgl/layout/symbol_projection.cpp
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -94,10 +94,12 @@ namespace mbgl {
}
- Point<float> project(const Point<float>& point, const mat4& matrix) {
+ typedef std::pair<Point<float>,float> PointAndCameraDistance;
+
+ PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) {
vec4 pos = {{ point.x, point.y, 0, 1 }};
matrix::transformMat4(pos, pos, matrix);
- return { static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) };
+ return {{ static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) }, pos[3] };
}
float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) {
@@ -150,9 +152,20 @@ namespace mbgl {
NotEnoughRoom,
NeedsFlipping
};
+
+ Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) {
+ // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane
+ // If it did, that would mean our label extended all the way out from within the viewport to a (very distant)
+ // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the
+ // plane of the camera.
+ const Point<float> projectedUnitVertex = project(previousTilePoint + util::unit<float>(previousTilePoint - currentTilePoint), projectionMatrix).first;
+ const Point<float> projectedUnitSegment = previousProjectedPoint - projectedUnitVertex;
+
+ return previousProjectedPoint + (projectedUnitSegment * (minimumLength / util::mag<float>(projectedUnitSegment)));
+ }
optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip,
- Point<float> anchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) {
+ const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) {
const float combinedOffsetX = flip ?
offsetX - lineOffsetX :
@@ -172,8 +185,8 @@ namespace mbgl {
int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1;
- Point<float> current = anchorPoint;
- Point<float> prev = anchorPoint;
+ Point<float> current = projectedAnchorPoint;
+ Point<float> prev = projectedAnchorPoint;
float distanceToPrev = 0.0;
float currentSegmentDistance = 0.0;
const float absOffsetX = std::abs(combinedOffsetX);
@@ -185,7 +198,18 @@ namespace mbgl {
if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) return {};
prev = current;
- current = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix);
+ PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix);
+ if (projection.second > 0) {
+ current = projection.first;
+ } else {
+ // The vertex is behind the plane of the camera, so we can't project it
+ // Instead, we'll create a vertex along the line that's far enough to include the glyph
+ const Point<float> previousTilePoint = distanceToPrev == 0 ?
+ tileAnchorPoint :
+ convertPoint<float>(line.at(currentIndex - dir));
+ const Point<float> currentTilePoint = convertPoint<float>(line.at(currentIndex));
+ current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix);
+ }
distanceToPrev += currentSegmentDistance;
currentSegmentDistance = util::dist<float>(prev, current);
@@ -211,29 +235,28 @@ namespace mbgl {
const mat4& posMatrix,
const mat4& labelPlaneMatrix,
const mat4& glCoordMatrix,
- gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray,
+ const Point<float>& projectedAnchorPoint) {
const float fontScale = fontSize / 24.0;
const float lineOffsetX = symbol.lineOffset[0] * fontSize;
const float lineOffsetY = symbol.lineOffset[1] * fontSize;
- const Point<float> anchorPoint = project(symbol.anchorPoint, labelPlaneMatrix);
-
std::vector<PlacedGlyph> placedGlyphs;
if (symbol.glyphOffsets.size() > 1) {
const float firstGlyphOffset = symbol.glyphOffsets.front();
const float lastGlyphOffset = symbol.glyphOffsets.back();
- optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
if (!firstPlacedGlyph)
return PlacementResult::NotEnoughRoom;
- optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
if (!lastPlacedGlyph)
return PlacementResult::NotEnoughRoom;
- const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix);
- const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix);
+ const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first;
+ const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first;
if (keepUpright && !flip &&
(symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) {
@@ -244,7 +267,7 @@ namespace mbgl {
for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) {
const float glyphOffsetX = symbol.glyphOffsets[glyphIndex];
// Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed
- auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
placedGlyphs.push_back(*placedGlyph);
}
placedGlyphs.push_back(*lastPlacedGlyph);
@@ -252,15 +275,23 @@ namespace mbgl {
// Only a single glyph to place
// So, determine whether to flip based on projected angle of the line segment it's on
if (keepUpright && !flip) {
- const Point<float> a = project(convertPoint<float>(symbol.line.at(symbol.segment)), posMatrix);
- const Point<float> b = project(convertPoint<float>(symbol.line.at(symbol.segment + 1)), posMatrix);
+ const Point<float> a = project(symbol.anchorPoint, posMatrix).first;
+ const Point<float> tileSegmentEnd = convertPoint<float>(symbol.line.at(symbol.segment + 1));
+ const PointAndCameraDistance projectedVertex = project(tileSegmentEnd, posMatrix);
+ // We know the anchor will be in the viewport, but the end of the line segment may be
+ // behind the plane of the camera, in which case we can use a point at any arbitrary (closer)
+ // point on the segment.
+ const Point<float> b = (projectedVertex.second > 0) ?
+ projectedVertex.first :
+ projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix);
+
if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) {
return PlacementResult::NeedsFlipping;
}
}
assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all
const float glyphOffsetX = symbol.glyphOffsets.front();
- optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment,
+ optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment,
symbol.line, labelPlaneMatrix);
if (!singleGlyph)
return PlacementResult::NotEnoughRoom;
@@ -275,6 +306,7 @@ namespace mbgl {
return PlacementResult::OK;
}
+
void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols,
const mat4& posMatrix, const style::SymbolPropertyValues& values,
const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) {
@@ -311,12 +343,14 @@ namespace mbgl {
const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ?
fontSize * perspectiveRatio :
fontSize / perspectiveRatio;
+
+ const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first;
- PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray);
+ PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint);
if (placeUnflipped == PlacementResult::NotEnoughRoom ||
(placeUnflipped == PlacementResult::NeedsFlipping &&
- placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray) == PlacementResult::NotEnoughRoom)) {
+ placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint) == PlacementResult::NotEnoughRoom)) {
hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
}
}
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 7d57f6863e..6eb555ad1e 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -50,7 +50,7 @@ public:
// StyleObserver
void onSourceChanged(style::Source&) override;
- void onUpdate(Update) override;
+ void onUpdate() override;
void onStyleLoading() override;
void onStyleLoaded() override;
void onStyleError(std::exception_ptr) override;
@@ -166,11 +166,18 @@ void Map::renderStill(StillImageCallback callback) {
impl->stillImageRequest = std::make_unique<StillImageRequest>(std::move(callback));
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
+}
+
+void Map::renderStill(const CameraOptions& camera, MapDebugOptions debugOptions, StillImageCallback callback) {
+ impl->cameraMutated = true;
+ impl->debugOptions = debugOptions;
+ impl->transform.jumpTo(camera);
+ renderStill(std::move(callback));
}
void Map::triggerRepaint() {
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
#pragma mark - Map::Impl RendererObserver
@@ -194,8 +201,11 @@ void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepai
observer.onDidFinishRenderingFrame(MapObserver::RenderMode(renderMode));
if (needsRepaint || transform.inTransition()) {
- onUpdate(Update::Repaint);
+ onUpdate();
}
+ } else if (stillImageRequest && rendererFullyLoaded) {
+ auto request = std::move(stillImageRequest);
+ request->callback(nullptr);
}
}
@@ -206,9 +216,6 @@ void Map::Impl::onDidFinishRenderingMap() {
loading = false;
observer.onDidFinishLoadingMap();
}
- } else if (stillImageRequest) {
- auto request = std::move(stillImageRequest);
- request->callback(nullptr);
}
};
@@ -233,12 +240,12 @@ void Map::setStyle(std::unique_ptr<Style> style) {
void Map::cancelTransitions() {
impl->transform.cancelTransitions();
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setGestureInProgress(bool inProgress) {
impl->transform.setGestureInProgress(inProgress);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
bool Map::isGestureInProgress() const {
@@ -266,19 +273,19 @@ CameraOptions Map::getCameraOptions(const EdgeInsets& padding) const {
void Map::jumpTo(const CameraOptions& camera) {
impl->cameraMutated = true;
impl->transform.jumpTo(camera);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.easeTo(camera, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.flyTo(camera, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
#pragma mark - Position
@@ -286,7 +293,7 @@ void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation)
void Map::moveBy(const ScreenCoordinate& point, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.moveBy(point, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) {
@@ -297,13 +304,13 @@ void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) {
void Map::setLatLng(const LatLng& latLng, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLng(latLng, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLng(latLng, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
LatLng Map::getLatLng(const EdgeInsets& padding) const {
@@ -319,7 +326,7 @@ void Map::resetPosition(const EdgeInsets& padding) {
camera.padding = padding;
camera.zoom = 0;
impl->transform.jumpTo(camera);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
@@ -333,13 +340,13 @@ void Map::setZoom(double zoom, const AnimationOptions& animation) {
void Map::setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setZoom(zoom, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setZoom(double zoom, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setZoom(zoom, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getZoom() const {
@@ -354,30 +361,30 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const AnimationOption
void Map::setLatLngZoom(const LatLng& latLng, double zoom, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLngZoom(latLng, zoom, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
-CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding) const {
+CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding, optional<double> bearing) const {
return cameraForLatLngs({
bounds.northwest(),
bounds.southwest(),
bounds.southeast(),
bounds.northeast(),
- }, padding);
+ }, padding, bearing);
}
-CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding) const {
+CameraOptions cameraForLatLngs(const std::vector<LatLng>& latLngs, const Transform& transform, const EdgeInsets& padding) {
CameraOptions options;
if (latLngs.empty()) {
return options;
}
-
+ Size size = transform.getState().getSize();
// Calculate the bounds of the possibly rotated shape with respect to the viewport.
ScreenCoordinate nePixel = {-INFINITY, -INFINITY};
ScreenCoordinate swPixel = {INFINITY, INFINITY};
- double viewportHeight = getSize().height;
+ double viewportHeight = size.height;
for (LatLng latLng : latLngs) {
- ScreenCoordinate pixel = impl->transform.latLngToScreenCoordinate(latLng);
+ ScreenCoordinate pixel = transform.latLngToScreenCoordinate(latLng);
swPixel.x = std::min(swPixel.x, pixel.x);
nePixel.x = std::max(nePixel.x, pixel.x);
swPixel.y = std::min(swPixel.y, viewportHeight - pixel.y);
@@ -389,14 +396,14 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
// Calculate the zoom level.
double minScale = INFINITY;
if (width > 0 || height > 0) {
- double scaleX = double(getSize().width) / width;
- double scaleY = double(getSize().height) / height;
+ double scaleX = double(size.width) / width;
+ double scaleY = double(size.height) / height;
scaleX -= (padding.left() + padding.right()) / width;
scaleY -= (padding.top() + padding.bottom()) / height;
minScale = util::min(scaleX, scaleY);
}
- double zoom = getZoom() + util::log2(minScale);
- zoom = util::clamp(zoom, getMinZoom(), getMaxZoom());
+ double zoom = transform.getZoom() + util::log2(minScale);
+ zoom = util::clamp(zoom, transform.getState().getMinZoom(), transform.getState().getMaxZoom());
// Calculate the center point of a virtual bounds that is extended in all directions by padding.
ScreenCoordinate centerPixel = nePixel + swPixel;
@@ -414,11 +421,34 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
// CameraOptions origin is at the top-left corner.
centerPixel.y = viewportHeight - centerPixel.y;
- options.center = latLngForPixel(centerPixel);
+ options.center = transform.screenCoordinateToLatLng(centerPixel);
options.zoom = zoom;
return options;
}
+CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding, optional<double> bearing) const {
+ if(bearing) {
+ double angle = -*bearing * util::DEG2RAD; // Convert to radians
+ Transform transform(impl->transform.getState());
+ transform.setAngle(angle);
+ CameraOptions options = mbgl::cameraForLatLngs(latLngs, transform, padding);
+ options.angle = angle;
+ return options;
+ } else {
+ return mbgl::cameraForLatLngs(latLngs, impl->transform, padding);
+ }
+}
+
+CameraOptions Map::cameraForGeometry(const Geometry<double>& geometry, const EdgeInsets& padding, optional<double> bearing) const {
+
+ std::vector<LatLng> latLngs;
+ forEachPoint(geometry, [&](const Point<double>& pt) {
+ latLngs.push_back({ pt.y, pt.x });
+ });
+ return cameraForLatLngs(latLngs, padding, bearing);
+
+}
+
LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const {
Transform shallow { impl->transform.getState() };
Size size = shallow.getState().getSize();
@@ -444,7 +474,7 @@ optional<LatLngBounds> Map::getLatLngBounds() const {
void Map::setLatLngBounds(optional<LatLngBounds> bounds) {
impl->cameraMutated = true;
impl->transform.setLatLngBounds(bounds);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setMinZoom(const double minZoom) {
@@ -495,7 +525,7 @@ double Map::getMaxPitch() const {
void Map::setSize(const Size size) {
impl->transform.resize(size);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
Size Map::getSize() const {
@@ -507,7 +537,7 @@ Size Map::getSize() const {
void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.rotateBy(first, second, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setBearing(double degrees, const AnimationOptions& animation) {
@@ -518,13 +548,13 @@ void Map::setBearing(double degrees, const AnimationOptions& animation) {
void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setBearing(double degrees, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(-degrees * util::DEG2RAD, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getBearing() const {
@@ -534,7 +564,7 @@ double Map::getBearing() const {
void Map::resetNorth(const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(0, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
#pragma mark - Pitch
@@ -547,7 +577,7 @@ void Map::setPitch(double pitch, const AnimationOptions& animation) {
void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setPitch(pitch * util::DEG2RAD, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getPitch() const {
@@ -558,7 +588,7 @@ double Map::getPitch() const {
void Map::setNorthOrientation(NorthOrientation orientation) {
impl->transform.setNorthOrientation(orientation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
NorthOrientation Map::getNorthOrientation() const {
@@ -569,7 +599,7 @@ NorthOrientation Map::getNorthOrientation() const {
void Map::setConstrainMode(mbgl::ConstrainMode mode) {
impl->transform.setConstrainMode(mode);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
ConstrainMode Map::getConstrainMode() const {
@@ -580,7 +610,7 @@ ConstrainMode Map::getConstrainMode() const {
void Map::setViewportMode(mbgl::ViewportMode mode) {
impl->transform.setViewportMode(mode);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
ViewportMode Map::getViewportMode() const {
@@ -618,24 +648,26 @@ double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
AnnotationID Map::addAnnotation(const Annotation& annotation) {
auto result = impl->annotationManager.addAnnotation(annotation, getMaxZoom());
- impl->onUpdate(Update::AnnotationData);
+ impl->onUpdate();
return result;
}
void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) {
- impl->onUpdate(impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom()));
+ if (impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom())) {
+ impl->onUpdate();
+ }
}
void Map::removeAnnotation(AnnotationID annotation) {
impl->annotationManager.removeAnnotation(annotation);
- impl->onUpdate(Update::AnnotationData);
+ impl->onUpdate();
}
#pragma mark - Toggles
void Map::setDebug(MapDebugOptions debugOptions) {
impl->debugOptions = debugOptions;
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::cycleDebugOptions() {
@@ -659,7 +691,7 @@ void Map::cycleDebugOptions() {
else
impl->debugOptions = MapDebugOptions::TileBorders;
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
MapDebugOptions Map::getDebug() const {
@@ -683,18 +715,14 @@ void Map::Impl::onSourceChanged(style::Source& source) {
}
void Map::Impl::onInvalidate() {
- onUpdate(Update::Repaint);
+ onUpdate();
}
-void Map::Impl::onUpdate(Update flags) {
+void Map::Impl::onUpdate() {
TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max();
transform.updateTransitions(timePoint);
- if (flags & Update::AnnotationData) {
- annotationManager.updateData();
- }
-
UpdateParameters params = {
style->impl->isLoaded(),
mode,
@@ -709,8 +737,6 @@ void Map::Impl::onUpdate(Update flags) {
style->impl->getImageImpls(),
style->impl->getSourceImpls(),
style->impl->getLayerImpls(),
- scheduler,
- fileSource,
annotationManager,
prefetchZoomDelta,
bool(stillImageRequest)
@@ -727,11 +753,7 @@ void Map::Impl::onStyleLoading() {
void Map::Impl::onStyleLoaded() {
if (!cameraMutated) {
- // Zoom first because it may constrain subsequent operations.
- map.setZoom(style->getDefaultZoom());
- map.setLatLng(style->getDefaultLatLng());
- map.setBearing(style->getDefaultBearing());
- map.setPitch(style->getDefaultPitch());
+ map.jumpTo(style->getDefaultCamera());
}
annotationManager.onStyleLoaded();
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index 4606e3eece..d1a320beae 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -132,7 +132,7 @@ double TransformState::getZoom() const {
return scaleZoom(scale);
}
-int32_t TransformState::getIntegerZoom() const {
+uint8_t TransformState::getIntegerZoom() const {
return getZoom();
}
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index f35f570549..59522d89fd 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -47,7 +47,7 @@ public:
// Zoom
double getZoom() const;
- int32_t getIntegerZoom() const;
+ uint8_t getIntegerZoom() const;
double getZoomFraction() const;
// Bounds
@@ -96,7 +96,7 @@ private:
// Limit the amount of zooming possible on the map.
double min_scale = std::pow(2, 0);
- double max_scale = std::pow(2, 20);
+ double max_scale = std::pow(2, util::DEFAULT_MAX_ZOOM);
double min_pitch = 0.0;
double max_pitch = util::PITCH_MAX;
diff --git a/src/mbgl/map/update.hpp b/src/mbgl/map/update.hpp
deleted file mode 100644
index 057720a5c9..0000000000
--- a/src/mbgl/map/update.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <mbgl/util/traits.hpp>
-#include <mbgl/util/util.hpp>
-
-namespace mbgl {
-
-enum class Update {
- Nothing = 0,
- Repaint = 1 << 0,
- AnnotationData = 1 << 7
-};
-
-MBGL_CONSTEXPR Update operator|(Update lhs, Update rhs) {
- return Update(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs));
-}
-
-MBGL_CONSTEXPR Update& operator|=(Update& lhs, const Update& rhs) {
- return (lhs = lhs | rhs);
-}
-
-MBGL_CONSTEXPR bool operator& (Update lhs, Update rhs) {
- return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/map/zoom_history.hpp b/src/mbgl/map/zoom_history.hpp
index 697e28573c..7821499d72 100644
--- a/src/mbgl/map/zoom_history.hpp
+++ b/src/mbgl/map/zoom_history.hpp
@@ -8,34 +8,39 @@ namespace mbgl {
struct ZoomHistory {
float lastZoom;
+ float lastFloorZoom;
float lastIntegerZoom;
TimePoint lastIntegerZoomTime;
bool first = true;
bool update(float z, const TimePoint& now) {
constexpr TimePoint zero = TimePoint(Duration::zero());
+ const float floorZ = std::floor(z);
+
if (first) {
first = false;
- lastIntegerZoom = std::floor(z);
+ lastIntegerZoom = floorZ;
lastIntegerZoomTime = zero;
lastZoom = z;
+ lastFloorZoom = floorZ;
+ return true;
+ }
+
+ if (lastFloorZoom > floorZ) {
+ lastIntegerZoom = floorZ + 1;
+ lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
+ } else if (lastFloorZoom < floorZ) {
+ lastIntegerZoom = floorZ;
+ lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
+ }
+
+ if (z != lastZoom) {
+ lastZoom = z;
+ lastFloorZoom = floorZ;
return true;
- } else {
- if (std::floor(lastZoom) < std::floor(z)) {
- lastIntegerZoom = std::floor(z);
- lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
- } else if (std::floor(lastZoom) > std::floor(z)) {
- lastIntegerZoom = std::floor(z + 1);
- lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
- }
-
- if (z != lastZoom) {
- lastZoom = z;
- return true;
- }
-
- return false;
}
+
+ return false;
}
};
diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp
index 684d9d6099..d023ec7d83 100644
--- a/src/mbgl/programs/attributes.hpp
+++ b/src/mbgl/programs/attributes.hpp
@@ -23,6 +23,7 @@ inline uint16_t packUint8Pair(T a, T b) {
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude);
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_normal);
MBGL_DEFINE_ATTRIBUTE(float, 3, a_projected_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_label_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_anchor_pos);
diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp
index db5c916d32..faf57ef19b 100644
--- a/src/mbgl/programs/line_program.cpp
+++ b/src/mbgl/programs/line_program.cpp
@@ -10,7 +10,7 @@ namespace mbgl {
using namespace style;
-static_assert(sizeof(LineLayoutVertex) == 8, "expected LineLayoutVertex size");
+static_assert(sizeof(LineLayoutVertex) == 12, "expected LineLayoutVertex size");
template <class Values, class...Args>
Values makeValues(const RenderLinePaintProperties::PossiblyEvaluated& properties,
diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp
index ed4a09bf10..da9964e623 100644
--- a/src/mbgl/programs/line_program.hpp
+++ b/src/mbgl/programs/line_program.hpp
@@ -30,7 +30,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_gl_units_to_pixels);
} // namespace uniforms
struct LineLayoutAttributes : gl::Attributes<
- attributes::a_pos,
+ attributes::a_pos_normal,
attributes::a_data<uint8_t, 4>>
{};
@@ -50,14 +50,17 @@ public:
/*
* @param p vertex position
* @param e extrude normal
- * @param t texture normal
+ * @param round whether the vertex uses a round line cap
+ * @param up whether the line normal points up or down
* @param dir direction of the line cap (-1/0/1)
*/
- static LayoutVertex layoutVertex(Point<int16_t> p, Point<double> e, Point<bool> t, int8_t dir, int32_t linesofar = 0) {
+ static LayoutVertex layoutVertex(Point<int16_t> p, Point<double> e, bool round, bool up, int8_t dir, int32_t linesofar = 0) {
return LayoutVertex {
{{
- static_cast<int16_t>((p.x * 2) | t.x),
- static_cast<int16_t>((p.y * 2) | t.y)
+ p.x,
+ p.y,
+ static_cast<int16_t>(round ? 1 : 0),
+ static_cast<int16_t>(up ? 1 : -1)
}},
{{
// add 128 to store a byte in an unsigned byte
diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp
index 194b012eee..a96518df38 100644
--- a/src/mbgl/renderer/buckets/line_bucket.cpp
+++ b/src/mbgl/renderer/buckets/line_bucket.cpp
@@ -401,7 +401,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate,
Point<double> extrude = normal;
if (endLeft)
extrude = extrude - (util::perp(normal) * endLeft);
- vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, false }, endLeft, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, false, endLeft, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -412,7 +412,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate,
extrude = normal * -1.0;
if (endRight)
extrude = extrude - (util::perp(normal) * endRight);
- vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, true }, -endRight, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, true, -endRight, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -437,7 +437,7 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex,
std::size_t startVertex,
std::vector<TriangleElement>& triangleStore) {
Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0);
- vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, { false, lineTurnsLeft }, 0, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, false, lineTurnsLeft, 0, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
diff --git a/src/mbgl/renderer/buckets/raster_bucket.cpp b/src/mbgl/renderer/buckets/raster_bucket.cpp
index 1a0409f456..a66dd42d74 100644
--- a/src/mbgl/renderer/buckets/raster_bucket.cpp
+++ b/src/mbgl/renderer/buckets/raster_bucket.cpp
@@ -69,16 +69,11 @@ void RasterBucket::setMask(TileMask&& mask_) {
for (const auto& id : mask) {
// Create a quad for every masked tile.
const int32_t vertexExtent = util::EXTENT >> id.z;
- const int32_t textureExtent = 32768 >> id.z;
const Point<int16_t> tlVertex = { static_cast<int16_t>(id.x * vertexExtent),
static_cast<int16_t>(id.y * vertexExtent) };
const Point<int16_t> brVertex = { static_cast<int16_t>(tlVertex.x + vertexExtent),
static_cast<int16_t>(tlVertex.y + vertexExtent) };
- const Point<uint16_t> tlTexture = { static_cast<uint16_t>(id.x * textureExtent),
- static_cast<uint16_t>(id.y * textureExtent) };
- const Point<uint16_t> brTexture = { static_cast<uint16_t>(tlTexture.x + textureExtent),
- static_cast<uint16_t>(tlTexture.y + textureExtent) };
if (segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
// Move to a new segments because the old one can't hold the geometry.
@@ -86,13 +81,13 @@ void RasterBucket::setMask(TileMask&& mask_) {
}
vertices.emplace_back(
- RasterProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { tlTexture.x, tlTexture.y }));
+ RasterProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(tlVertex.y) }));
vertices.emplace_back(
- RasterProgram::layoutVertex({ brVertex.x, tlVertex.y }, { brTexture.x, tlTexture.y }));
+ RasterProgram::layoutVertex({ brVertex.x, tlVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(tlVertex.y) }));
vertices.emplace_back(
- RasterProgram::layoutVertex({ tlVertex.x, brVertex.y }, { tlTexture.x, brTexture.y }));
+ RasterProgram::layoutVertex({ tlVertex.x, brVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(brVertex.y) }));
vertices.emplace_back(
- RasterProgram::layoutVertex({ brVertex.x, brVertex.y }, { brTexture.x, brTexture.y }));
+ RasterProgram::layoutVertex({ brVertex.x, brVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(brVertex.y) }));
auto& segment = segments.back();
assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp
index 692747bca4..2ef6be0c4f 100644
--- a/src/mbgl/renderer/image_manager.cpp
+++ b/src/mbgl/renderer/image_manager.cpp
@@ -39,6 +39,13 @@ void ImageManager::removeImage(const std::string& id) {
auto it = patterns.find(id);
if (it != patterns.end()) {
+ // Clear pattern from the atlas image.
+ const uint32_t x = it->second.bin->x;
+ const uint32_t y = it->second.bin->y;
+ const uint32_t w = it->second.bin->w;
+ const uint32_t h = it->second.bin->h;
+ PremultipliedImage::clear(atlasImage, { x, y }, { w, h });
+
shelfPack.unref(*it->second.bin);
patterns.erase(it);
}
@@ -52,23 +59,23 @@ const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
return nullptr;
}
-void ImageManager::getImages(ImageRequestor& requestor, ImageDependencies dependencies) {
+void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) {
// If the sprite has been loaded, or if all the icon dependencies are already present
// (i.e. if they've been addeded via runtime styling), then notify the requestor immediately.
// Otherwise, delay notification until the sprite is loaded. At that point, if any of the
// dependencies are still unavailable, we'll just assume they are permanently missing.
bool hasAllDependencies = true;
if (!isLoaded()) {
- for (const auto& dependency : dependencies) {
+ for (const auto& dependency : pair.first) {
if (images.find(dependency) == images.end()) {
hasAllDependencies = false;
}
}
}
if (isLoaded() || hasAllDependencies) {
- notify(requestor, dependencies);
+ notify(requestor, std::move(pair));
} else {
- requestors.emplace(&requestor, std::move(dependencies));
+ requestors.emplace(&requestor, std::move(pair));
}
}
@@ -76,17 +83,17 @@ void ImageManager::removeRequestor(ImageRequestor& requestor) {
requestors.erase(&requestor);
}
-void ImageManager::notify(ImageRequestor& requestor, const ImageDependencies& dependencies) const {
+void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const {
ImageMap response;
- for (const auto& dependency : dependencies) {
+ for (const auto& dependency : pair.first) {
auto it = images.find(dependency);
if (it != images.end()) {
response.emplace(*it);
}
}
- requestor.onImagesAvailable(response);
+ requestor.onImagesAvailable(response, pair.second);
}
void ImageManager::dumpDebugLogs() const {
diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp
index 1c9d67f47d..f72ba9fb53 100644
--- a/src/mbgl/renderer/image_manager.hpp
+++ b/src/mbgl/renderer/image_manager.hpp
@@ -21,7 +21,7 @@ class Context;
class ImageRequestor {
public:
virtual ~ImageRequestor() = default;
- virtual void onImagesAvailable(ImageMap) = 0;
+ virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0;
};
/*
@@ -50,15 +50,15 @@ public:
void updateImage(Immutable<style::Image::Impl>);
void removeImage(const std::string&);
- void getImages(ImageRequestor&, ImageDependencies);
+ void getImages(ImageRequestor&, ImageRequestPair&&);
void removeRequestor(ImageRequestor&);
private:
- void notify(ImageRequestor&, const ImageDependencies&) const;
+ void notify(ImageRequestor&, const ImageRequestPair&) const;
bool loaded = false;
- std::unordered_map<ImageRequestor*, ImageDependencies> requestors;
+ std::unordered_map<ImageRequestor*, ImageRequestPair> requestors;
ImageMap images;
// Pattern stuff
diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp
index ae0c4b026b..7ece3970da 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.cpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -16,8 +16,12 @@ RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
RenderCustomLayer::~RenderCustomLayer() {
assert(BackendScope::exists());
- if (initialized && impl().deinitializeFn) {
- impl().deinitializeFn(impl().context);
+ if (initialized) {
+ if (contextDestroyed && impl().contextLostFn ) {
+ impl().contextLostFn(impl().context);
+ } else if (!contextDestroyed && impl().deinitializeFn) {
+ impl().deinitializeFn(impl().context);
+ }
}
}
diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp
index d8e9d93811..32ed9da8da 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.hpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.hpp
@@ -19,8 +19,13 @@ public:
const style::CustomLayer::Impl& impl() const;
+ void markContextDestroyed() {
+ contextDestroyed = true;
+ };
+
private:
bool initialized = false;
+ bool contextDestroyed = false;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
index 6295f62b21..fbd6160e8a 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -37,8 +37,9 @@ void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters
void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
evaluated = unevaluated.evaluate(parameters);
- passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent
- : RenderPass::None;
+ passes = (evaluated.get<style::FillExtrusionOpacity>() > 0)
+ ? (RenderPass::Translucent | RenderPass::Pass3D)
+ : RenderPass::None;
}
bool RenderFillExtrusionLayer::hasTransition() const {
@@ -50,113 +51,100 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*
return;
}
- const auto size = parameters.context.viewport.getCurrentValue().size;
+ if (parameters.pass == RenderPass::Pass3D) {
+ const auto& size = parameters.staticData.backendSize;
- if (!parameters.staticData.extrusionTexture || parameters.staticData.extrusionTexture->getSize() != size) {
- parameters.staticData.extrusionTexture = OffscreenTexture(parameters.context, size, OffscreenTextureAttachment::Depth);
- }
-
- parameters.staticData.extrusionTexture->bind();
-
- parameters.context.setStencilMode(gl::StencilMode::disabled());
- parameters.context.setDepthMode(parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite));
- parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {});
-
- if (evaluated.get<FillExtrusionPattern>().from.empty()) {
- for (const RenderTile& tile : renderTiles) {
- assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
- FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
-
- parameters.programs.fillExtrusion.get(evaluated).draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
- FillExtrusionUniforms::values(
- tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
- evaluated.get<FillExtrusionTranslateAnchor>(),
- parameters.state),
- parameters.state,
- parameters.evaluatedLight
- ),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.triangleSegments,
- bucket.paintPropertyBinders.at(getID()),
- evaluated,
- parameters.state.getZoom(),
- getID());
+ if (!renderTexture || renderTexture->getSize() != size) {
+ renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer);
}
- } else {
- optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from);
- optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to);
- if (!imagePosA || !imagePosB) {
- return;
+ renderTexture->bind();
+
+ optional<float> depthClearValue = {};
+ if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0;
+ // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass.
+ parameters.staticData.depthRenderbuffer->shouldClear(false);
+
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {});
+
+ if (evaluated.get<FillExtrusionPattern>().from.empty()) {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusion.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.state, parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
+ } else {
+ optional<ImagePosition> imagePosA =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from);
+ optional<ImagePosition> imagePosB =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusionPattern.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionPatternUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB,
+ evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state,
+ -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
+ parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
}
- parameters.imageManager.bind(parameters.context, 0);
-
- for (const RenderTile& tile : renderTiles) {
- assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
- FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
-
- parameters.programs.fillExtrusionPattern.get(evaluated).draw(
- parameters.context,
- gl::Triangles(),
- parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
- FillExtrusionPatternUniforms::values(
- tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
- evaluated.get<FillExtrusionTranslateAnchor>(),
- parameters.state),
- parameters.imageManager.getPixelSize(),
- *imagePosA,
- *imagePosB,
- evaluated.get<FillExtrusionPattern>(),
- tile.id,
- parameters.state,
- -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
- parameters.evaluatedLight
- ),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.triangleSegments,
- bucket.paintPropertyBinders.at(getID()),
- evaluated,
- parameters.state.getZoom(),
- getID());
- }
- }
+ } else if (parameters.pass == RenderPass::Translucent) {
+ parameters.context.bindTexture(renderTexture->getTexture());
- parameters.backend.bind();
- parameters.context.bindTexture(parameters.staticData.extrusionTexture->getTexture());
-
- mat4 viewportMat;
- matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
-
- const Properties<>::PossiblyEvaluated properties;
-
- parameters.programs.extrusionTexture.draw(
- parameters.context,
- gl::Triangles(),
- gl::DepthMode::disabled(),
- gl::StencilMode::disabled(),
- parameters.colorModeForRenderPass(),
- ExtrusionTextureProgram::UniformValues{
- uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
- uniforms::u_image::Value{ 0 },
- uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() }
- },
- parameters.staticData.extrusionTextureVertexBuffer,
- parameters.staticData.quadTriangleIndexBuffer,
- parameters.staticData.extrusionTextureSegments,
- ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 },
- properties,
- parameters.state.getZoom(),
- getID());
+ const auto& size = parameters.staticData.backendSize;
+
+ mat4 viewportMat;
+ matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
+
+ const Properties<>::PossiblyEvaluated properties;
+
+ parameters.programs.extrusionTexture.draw(
+ parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ ExtrusionTextureProgram::UniformValues{
+ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } },
+ parameters.staticData.extrusionTextureVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.extrusionTextureSegments,
+ ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
+ parameters.state.getZoom(), getID());
+ }
}
bool RenderFillExtrusionLayer::queryIntersectsFeature(
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
index a53e00ca6f..838494cf91 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -3,6 +3,8 @@
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/offscreen_texture.hpp>
namespace mbgl {
@@ -30,6 +32,8 @@ public:
style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;
const style::FillExtrusionLayer::Impl& impl() const;
+
+ optional<OffscreenTexture> renderTexture;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
index 2a61a9e993..22cb9563c1 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -64,15 +64,15 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
- auto draw = [&] (uint8_t sublayer,
- auto& program,
+ auto draw = [&] (auto& program,
const auto& drawMode,
+ const auto& depthMode,
const auto& indexBuffer,
const auto& segments) {
program.get(evaluated).draw(
parameters.context,
drawMode,
- parameters.depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
+ depthMode,
parameters.stencilModeForClipping(tile.clip),
parameters.colorModeForRenderPass(),
FillProgram::UniformValues {
@@ -93,29 +93,25 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
);
};
- if (evaluated.get<FillAntialias>() && !unevaluated.get<FillOutlineColor>().isUndefined() && parameters.pass == RenderPass::Translucent) {
- draw(2,
- parameters.programs.fillOutline,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
- }
-
// Only draw the fill when it's opaque and we're drawing opaque fragments,
// or when it's translucent and we're drawing translucent fragments.
if ((evaluated.get<FillColor>().constantOr(Color()).a >= 1.0f
&& evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) {
- draw(1,
- parameters.programs.fill,
+ draw(parameters.programs.fill,
gl::Triangles(),
+ parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque
+ ? gl::DepthMode::ReadWrite
+ : gl::DepthMode::ReadOnly),
*bucket.triangleIndexBuffer,
bucket.triangleSegments);
}
- if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined() && parameters.pass == RenderPass::Translucent) {
- draw(2,
- parameters.programs.fillOutline,
- gl::Lines { 2.0f },
+ if (evaluated.get<FillAntialias>() && parameters.pass == RenderPass::Translucent) {
+ draw(parameters.programs.fillOutline,
+ gl::Lines{ 2.0f },
+ parameters.depthModeForSublayer(
+ unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0,
+ gl::DepthMode::ReadOnly),
*bucket.lineIndexBuffer,
bucket.lineSegments);
}
@@ -138,15 +134,15 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
- auto draw = [&] (uint8_t sublayer,
- auto& program,
+ auto draw = [&] (auto& program,
const auto& drawMode,
+ const auto& depthMode,
const auto& indexBuffer,
const auto& segments) {
program.get(evaluated).draw(
parameters.context,
drawMode,
- parameters.depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
+ depthMode,
parameters.stencilModeForClipping(tile.clip),
parameters.colorModeForRenderPass(),
FillPatternUniforms::values(
@@ -171,21 +167,19 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
);
};
- draw(0,
- parameters.programs.fillPattern,
+ draw(parameters.programs.fillPattern,
gl::Triangles(),
+ parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite),
*bucket.triangleIndexBuffer,
bucket.triangleSegments);
- if (!evaluated.get<FillAntialias>() || !unevaluated.get<FillOutlineColor>().isUndefined()) {
- continue;
+ if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined()) {
+ draw(parameters.programs.fillOutlinePattern,
+ gl::Lines { 2.0f },
+ parameters.depthModeForSublayer(2, gl::DepthMode::ReadOnly),
+ *bucket.lineIndexBuffer,
+ bucket.lineSegments);
}
-
- draw(2,
- parameters.programs.fillOutlinePattern,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
}
}
}
diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp
index ebdaecd3a3..299db844bc 100644
--- a/src/mbgl/renderer/paint_parameters.cpp
+++ b/src/mbgl/renderer/paint_parameters.cpp
@@ -1,6 +1,5 @@
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/render_style.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/map/transform_state.hpp>
@@ -11,17 +10,19 @@ PaintParameters::PaintParameters(gl::Context& context_,
GLContextMode contextMode_,
RendererBackend& backend_,
const UpdateParameters& updateParameters,
- RenderStyle& style,
+ const EvaluatedLight& evaluatedLight_,
RenderStaticData& staticData_,
- FrameHistory& frameHistory_)
+ FrameHistory& frameHistory_,
+ ImageManager& imageManager_,
+ LineAtlas& lineAtlas_)
: context(context_),
backend(backend_),
state(updateParameters.transformState),
- evaluatedLight(style.getRenderLight().getEvaluated()),
+ evaluatedLight(evaluatedLight_),
staticData(staticData_),
frameHistory(frameHistory_),
- imageManager(*style.imageManager),
- lineAtlas(*style.lineAtlas),
+ imageManager(imageManager_),
+ lineAtlas(lineAtlas_),
mapMode(updateParameters.mode),
debugOptions(updateParameters.debugOptions),
contextMode(contextMode_),
@@ -61,6 +62,10 @@ gl::DepthMode PaintParameters::depthModeForSublayer(uint8_t n, gl::DepthMode::Ma
return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } };
}
+gl::DepthMode PaintParameters::depthModeFor3D(gl::DepthMode::Mask mask) const {
+ return gl::DepthMode { gl::DepthMode::LessEqual, mask, { 0.0, 1.0 } };
+}
+
gl::StencilMode PaintParameters::stencilModeForClipping(const ClipID& id) const {
return gl::StencilMode {
gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) },
diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp
index e9d3562a75..4a2c2c6f12 100644
--- a/src/mbgl/renderer/paint_parameters.hpp
+++ b/src/mbgl/renderer/paint_parameters.hpp
@@ -15,7 +15,6 @@ namespace mbgl {
class RendererBackend;
class UpdateParameters;
-class RenderStyle;
class RenderStaticData;
class FrameHistory;
class Programs;
@@ -31,9 +30,11 @@ public:
GLContextMode,
RendererBackend&,
const UpdateParameters&,
- RenderStyle&,
+ const EvaluatedLight&,
RenderStaticData&,
- FrameHistory&);
+ FrameHistory&,
+ ImageManager&,
+ LineAtlas&);
gl::Context& context;
RendererBackend& backend;
@@ -59,6 +60,7 @@ public:
Programs& programs;
gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const;
+ gl::DepthMode depthModeFor3D(gl::DepthMode::Mask) const;
gl::StencilMode stencilModeForClipping(const ClipID&) const;
gl::ColorMode colorModeForRenderPass() const;
diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp
deleted file mode 100644
index 4bf5629263..0000000000
--- a/src/mbgl/renderer/render_item.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include <mbgl/util/color.hpp>
-
-#include <unordered_set>
-#include <vector>
-
-namespace mbgl {
-
-class RenderLayer;
-class RenderSource;
-class RenderTile;
-class Bucket;
-
-namespace style {
-} // namespace style
-
-class RenderItem {
-public:
- RenderItem(RenderLayer& layer_,
- RenderSource* renderSource_)
- : layer(layer_), source(renderSource_) {
- }
-
- RenderLayer& layer;
- RenderSource* source;
-};
-
-class RenderData {
-public:
- Color backgroundColor;
- std::unordered_set<RenderSource*> sources;
- std::vector<RenderItem> order;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp
index ae2b923ba1..5d18304129 100644
--- a/src/mbgl/renderer/render_pass.hpp
+++ b/src/mbgl/renderer/render_pass.hpp
@@ -11,6 +11,7 @@ enum class RenderPass : uint8_t {
None = 0,
Opaque = 1 << 0,
Translucent = 1 << 1,
+ Pass3D = 1 << 2,
};
MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) {
diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp
index b565439588..8293923ff6 100644
--- a/src/mbgl/renderer/render_source.hpp
+++ b/src/mbgl/renderer/render_source.hpp
@@ -18,7 +18,6 @@ namespace mbgl {
class PaintParameters;
class TransformState;
class RenderTile;
-class RenderStyle;
class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
@@ -63,7 +62,7 @@ public:
virtual std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const = 0;
virtual std::vector<Feature>
diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp
index 4c6028d7b6..ccf239e643 100644
--- a/src/mbgl/renderer/render_static_data.cpp
+++ b/src/mbgl/renderer/render_static_data.cpp
@@ -32,9 +32,9 @@ static gl::IndexVector<gl::LineStrip> tileLineStripIndices() {
static gl::VertexVector<RasterLayoutVertex> rasterVertices() {
gl::VertexVector<RasterLayoutVertex> result;
result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }));
- result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { 32767, 0 }));
- result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, 32767 }));
- result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { 32767, 32767 }));
+ result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { util::EXTENT, 0 }));
+ result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, util::EXTENT }));
+ result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { util::EXTENT, util::EXTENT }));
return result;
}
diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp
index 07a47b4c8f..cf58c31f4d 100644
--- a/src/mbgl/renderer/render_static_data.hpp
+++ b/src/mbgl/renderer/render_static_data.hpp
@@ -4,7 +4,6 @@
#include <mbgl/gl/index_buffer.hpp>
#include <mbgl/programs/programs.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/offscreen_texture.hpp>
#include <string>
@@ -26,7 +25,9 @@ public:
SegmentVector<RasterAttributes> rasterSegments;
SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
- optional<OffscreenTexture> extrusionTexture;
+ optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer;
+ bool has3D = false;
+ Size backendSize;
Programs programs;
diff --git a/src/mbgl/renderer/render_style.cpp b/src/mbgl/renderer/render_style.cpp
deleted file mode 100644
index 3d95b12bc4..0000000000
--- a/src/mbgl/renderer/render_style.cpp
+++ /dev/null
@@ -1,449 +0,0 @@
-#include <mbgl/renderer/render_style.hpp>
-#include <mbgl/renderer/render_style_observer.hpp>
-#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/transition_parameters.hpp>
-#include <mbgl/renderer/property_evaluation_parameters.hpp>
-#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_item.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_background_layer.hpp>
-#include <mbgl/renderer/layers/render_circle_layer.hpp>
-#include <mbgl/renderer/layers/render_custom_layer.hpp>
-#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
-#include <mbgl/renderer/layers/render_fill_layer.hpp>
-#include <mbgl/renderer/layers/render_line_layer.hpp>
-#include <mbgl/renderer/layers/render_raster_layer.hpp>
-#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/renderer/backend_scope.hpp>
-#include <mbgl/renderer/style_diff.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/renderer/query.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/style/source_impl.hpp>
-#include <mbgl/style/transition_options.hpp>
-#include <mbgl/sprite/sprite_loader.hpp>
-#include <mbgl/text/glyph_manager.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/logging.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-RenderStyleObserver nullObserver;
-
-RenderStyle::RenderStyle(Scheduler& scheduler_, FileSource& fileSource_)
- : scheduler(scheduler_),
- fileSource(fileSource_),
- glyphManager(std::make_unique<GlyphManager>(fileSource)),
- imageManager(std::make_unique<ImageManager>()),
- lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
- imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>()),
- sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()),
- layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()),
- renderLight(makeMutable<Light::Impl>()),
- observer(&nullObserver) {
- glyphManager->setObserver(this);
-}
-
-RenderStyle::~RenderStyle() {
- assert(BackendScope::exists()); // Required for custom layers.
-}
-
-void RenderStyle::setObserver(RenderStyleObserver* observer_) {
- observer = observer_;
-}
-
-std::vector<const RenderLayer*> RenderStyle::getRenderLayers() const {
- std::vector<const RenderLayer*> result;
- result.reserve(renderLayers.size());
- for (const auto& layer : *layerImpls) {
- result.push_back(getRenderLayer(layer->id));
- }
- return result;
-}
-
-RenderLayer* RenderStyle::getRenderLayer(const std::string& id) {
- auto it = renderLayers.find(id);
- return it != renderLayers.end() ? it->second.get() : nullptr;
-}
-
-const RenderLayer* RenderStyle::getRenderLayer(const std::string& id) const {
- auto it = renderLayers.find(id);
- return it != renderLayers.end() ? it->second.get() : nullptr;
-}
-
-const RenderLight& RenderStyle::getRenderLight() const {
- return renderLight;
-}
-
-void RenderStyle::update(const UpdateParameters& parameters) {
- assert(BackendScope::exists()); // Required for custom layers.
-
- const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint);
-
- const TransitionParameters transitionParameters {
- parameters.timePoint,
- parameters.mode == MapMode::Continuous ? parameters.transitionOptions : TransitionOptions()
- };
-
- const PropertyEvaluationParameters evaluationParameters {
- zoomHistory,
- parameters.timePoint,
- parameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero()
- };
-
- const TileParameters tileParameters {
- parameters.pixelRatio,
- parameters.debugOptions,
- parameters.transformState,
- parameters.scheduler,
- parameters.fileSource,
- parameters.mode,
- parameters.annotationManager,
- *imageManager,
- *glyphManager,
- parameters.prefetchZoomDelta
- };
-
- glyphManager->setURL(parameters.glyphURL);
-
- // Update light.
- const bool lightChanged = renderLight.impl != parameters.light;
-
- if (lightChanged) {
- renderLight.impl = parameters.light;
- renderLight.transition(transitionParameters);
- }
-
- if (lightChanged || zoomChanged || renderLight.hasTransition()) {
- renderLight.evaluate(evaluationParameters);
- }
-
-
- const ImageDifference imageDiff = diffImages(imageImpls, parameters.images);
- imageImpls = parameters.images;
-
- // Remove removed images from sprite atlas.
- for (const auto& entry : imageDiff.removed) {
- imageManager->removeImage(entry.first);
- }
-
- // Add added images to sprite atlas.
- for (const auto& entry : imageDiff.added) {
- imageManager->addImage(entry.second);
- }
-
- // Update changed images.
- for (const auto& entry : imageDiff.changed) {
- imageManager->updateImage(entry.second.after);
- }
-
- imageManager->setLoaded(parameters.spriteLoaded);
-
-
- const LayerDifference layerDiff = diffLayers(layerImpls, parameters.layers);
- layerImpls = parameters.layers;
-
- // Remove render layers for removed layers.
- for (const auto& entry : layerDiff.removed) {
- renderLayers.erase(entry.first);
- }
-
- // Create render layers for newly added layers.
- for (const auto& entry : layerDiff.added) {
- renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
- }
-
- // Update render layers for changed layers.
- for (const auto& entry : layerDiff.changed) {
- renderLayers.at(entry.first)->setImpl(entry.second.after);
- }
-
- // Update layers for class and zoom changes.
- for (const auto& entry : renderLayers) {
- RenderLayer& layer = *entry.second;
- const bool layerAdded = layerDiff.added.count(entry.first);
- const bool layerChanged = layerDiff.changed.count(entry.first);
-
- if (layerAdded || layerChanged) {
- layer.transition(transitionParameters);
- }
-
- if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
- layer.evaluate(evaluationParameters);
- }
- }
-
-
- const SourceDifference sourceDiff = diffSources(sourceImpls, parameters.sources);
- sourceImpls = parameters.sources;
-
- // Remove render layers for removed sources.
- for (const auto& entry : sourceDiff.removed) {
- renderSources.erase(entry.first);
- }
-
- // Create render sources for newly added sources.
- for (const auto& entry : sourceDiff.added) {
- std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
- renderSource->setObserver(this);
- renderSources.emplace(entry.first, std::move(renderSource));
- }
-
- // Update all sources.
- for (const auto& source : *sourceImpls) {
- std::vector<Immutable<Layer::Impl>> filteredLayers;
- bool needsRendering = false;
- bool needsRelayout = false;
-
- for (const auto& layer : *layerImpls) {
- if (layer->type == LayerType::Background ||
- layer->type == LayerType::Custom ||
- layer->source != source->id) {
- continue;
- }
-
- if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
- needsRendering = true;
- }
-
- if (!needsRelayout && (
- hasLayoutDifference(layerDiff, layer->id) ||
- !imageDiff.added.empty() ||
- !imageDiff.removed.empty() ||
- !imageDiff.changed.empty())) {
- needsRelayout = true;
- }
-
- filteredLayers.push_back(layer);
- }
-
- renderSources.at(source->id)->update(source,
- filteredLayers,
- needsRendering,
- needsRelayout,
- tileParameters);
- }
-}
-
-RenderSource* RenderStyle::getRenderSource(const std::string& id) const {
- auto it = renderSources.find(id);
- return it != renderSources.end() ? it->second.get() : nullptr;
-}
-
-bool RenderStyle::hasTransitions() const {
- if (renderLight.hasTransition()) {
- return true;
- }
-
- for (const auto& entry : renderLayers) {
- if (entry.second->hasTransition()) {
- return true;
- }
- }
-
- return false;
-}
-
-bool RenderStyle::isLoaded() const {
- for (const auto& entry: renderSources) {
- if (!entry.second->isLoaded()) {
- return false;
- }
- }
-
- if (!imageManager->isLoaded()) {
- return false;
- }
-
- return true;
-}
-
-RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle) {
- RenderData result;
-
- for (const auto& entry : renderSources) {
- if (entry.second->isEnabled()) {
- result.sources.insert(entry.second.get());
- }
- }
-
- for (auto& layerImpl : *layerImpls) {
- RenderLayer* layer = getRenderLayer(layerImpl->id);
- assert(layer);
-
- if (!layer->needsRendering(zoomHistory.lastZoom)) {
- continue;
- }
-
- if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
- if (debugOptions & MapDebugOptions::Overdraw) {
- // We want to skip glClear optimization in overdraw mode.
- result.order.emplace_back(*layer, nullptr);
- continue;
- }
- const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
- if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) {
- // This is a solid background. We can use glClear().
- result.backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
- } else {
- // This is a textured background, or not the bottommost layer. We need to render it with a quad.
- result.order.emplace_back(*layer, nullptr);
- }
- continue;
- }
-
- if (layer->is<RenderCustomLayer>()) {
- result.order.emplace_back(*layer, nullptr);
- continue;
- }
-
- RenderSource* source = getRenderSource(layer->baseImpl->source);
- if (!source) {
- Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
- continue;
- }
-
- const bool symbolLayer = layer->is<RenderSymbolLayer>();
-
- auto sortedTiles = source->getRenderTiles();
- if (symbolLayer) {
- // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn
- // on top of each other, with lower symbols being drawn on top of higher symbols.
- std::sort(sortedTiles.begin(), sortedTiles.end(),
- [angle](const RenderTile& a, const RenderTile& b) {
- Point<float> pa(a.id.canonical.x, a.id.canonical.y);
- Point<float> pb(b.id.canonical.x, b.id.canonical.y);
-
- auto par = util::rotate(pa, angle);
- auto pbr = util::rotate(pb, angle);
-
- return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x);
- });
- } else {
- std::sort(sortedTiles.begin(), sortedTiles.end(),
- [](const auto& a, const auto& b) { return a.get().id < b.get().id; });
- }
-
- std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
- for (auto& sortedTile : sortedTiles) {
- auto& tile = sortedTile.get();
- if (!tile.tile.isRenderable()) {
- continue;
- }
-
- // We're not clipping symbol layers, so when we have both parents and children of symbol
- // layers, we drop all children in favor of their parent to avoid duplicate labels.
- // See https://github.com/mapbox/mapbox-gl-native/issues/2482
- if (symbolLayer) {
- bool skip = false;
- // Look back through the buckets we decided to render to find out whether there is
- // already a bucket from this layer that is a parent of this tile. Tiles are ordered
- // by zoom level when we obtain them from getTiles().
- for (auto it = sortedTilesForInsertion.rbegin();
- it != sortedTilesForInsertion.rend(); ++it) {
- if (tile.tile.id.isChildOf(it->get().tile.id)) {
- skip = true;
- break;
- }
- }
- if (skip) {
- continue;
- }
- }
-
- auto bucket = tile.tile.getBucket(*layer->baseImpl);
- if (bucket) {
- sortedTilesForInsertion.emplace_back(tile);
- tile.used = true;
- }
- }
- layer->setRenderTiles(std::move(sortedTilesForInsertion));
- result.order.emplace_back(*layer, source);
- }
-
- return result;
-}
-
-std::vector<Feature> RenderStyle::queryRenderedFeatures(const ScreenLineString& geometry,
- const TransformState& transformState,
- const RenderedQueryOptions& options) const {
- std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
-
- if (options.layerIDs) {
- std::unordered_set<std::string> sourceIDs;
- for (const auto& layerID : *options.layerIDs) {
- if (const RenderLayer* layer = getRenderLayer(layerID)) {
- sourceIDs.emplace(layer->baseImpl->source);
- }
- }
- for (const auto& sourceID : sourceIDs) {
- if (RenderSource* renderSource = getRenderSource(sourceID)) {
- auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, *this, options);
- std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
- }
- }
- } else {
- for (const auto& entry : renderSources) {
- auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, *this, options);
- std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
- }
- }
-
- std::vector<Feature> result;
-
- if (resultsByLayer.empty()) {
- return result;
- }
-
- // Combine all results based on the style layer order.
- for (const auto& layerImpl : *layerImpls) {
- const RenderLayer* layer = getRenderLayer(layerImpl->id);
- if (!layer->needsRendering(zoomHistory.lastZoom)) {
- continue;
- }
- auto it = resultsByLayer.find(layer->baseImpl->id);
- if (it != resultsByLayer.end()) {
- std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
- }
- }
-
- return result;
-}
-
-void RenderStyle::onLowMemory() {
- for (const auto& entry : renderSources) {
- entry.second->onLowMemory();
- }
-}
-
-void RenderStyle::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
- Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
- glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
- observer->onResourceError(error);
-}
-
-void RenderStyle::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
- util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
- observer->onResourceError(error);
-}
-
-void RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) {
- observer->onInvalidate();
-}
-
-void RenderStyle::dumpDebugLogs() const {
- for (const auto& entry : renderSources) {
- entry.second->dumpDebugLogs();
- }
-
- imageManager->dumpDebugLogs();
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style.hpp b/src/mbgl/renderer/render_style.hpp
deleted file mode 100644
index 23a640c482..0000000000
--- a/src/mbgl/renderer/render_style.hpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#pragma once
-
-#include <mbgl/style/image.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_source_observer.hpp>
-#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/renderer/render_light.hpp>
-#include <mbgl/text/glyph_manager_observer.hpp>
-#include <mbgl/map/zoom_history.hpp>
-#include <mbgl/map/mode.hpp>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace mbgl {
-
-class FileSource;
-class GlyphManager;
-class ImageManager;
-class LineAtlas;
-class RenderData;
-class TransformState;
-class RenderedQueryOptions;
-class Scheduler;
-class UpdateParameters;
-class RenderStyleObserver;
-
-namespace style {
-class Image;
-class Source;
-class Layer;
-} // namespace style
-
-class RenderStyle : public GlyphManagerObserver,
- public RenderSourceObserver {
-public:
- RenderStyle(Scheduler&, FileSource&);
- ~RenderStyle() final;
-
- void setObserver(RenderStyleObserver*);
- void update(const UpdateParameters&);
-
- bool isLoaded() const;
- bool hasTransitions() const;
-
- RenderSource* getRenderSource(const std::string& id) const;
-
- std::vector<const RenderLayer*> getRenderLayers() const;
-
- RenderLayer* getRenderLayer(const std::string& id);
- const RenderLayer* getRenderLayer(const std::string& id) const;
-
- const RenderLight& getRenderLight() const;
-
- RenderData getRenderData(MapDebugOptions, float angle);
-
- std::vector<Feature> queryRenderedFeatures(const ScreenLineString& geometry,
- const TransformState& transformState,
- const RenderedQueryOptions& options) const;
-
- void onLowMemory();
-
- void dumpDebugLogs() const;
-
- Scheduler& scheduler;
- FileSource& fileSource;
- std::unique_ptr<GlyphManager> glyphManager;
- std::unique_ptr<ImageManager> imageManager;
- std::unique_ptr<LineAtlas> lineAtlas;
-
-private:
- Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;
- Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls;
- Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls;
-
- std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources;
- std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
- RenderLight renderLight;
-
- // GlyphManagerObserver implementation.
- void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
-
- // RenderSourceObserver implementation.
- void onTileChanged(RenderSource&, const OverscaledTileID&) override;
- void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
-
- RenderStyleObserver* observer;
- ZoomHistory zoomHistory;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style_observer.hpp b/src/mbgl/renderer/render_style_observer.hpp
deleted file mode 100644
index 5852dd68b8..0000000000
--- a/src/mbgl/renderer/render_style_observer.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <exception>
-
-namespace mbgl {
-
-class RenderStyleObserver {
-public:
- virtual ~RenderStyleObserver() = default;
- virtual void onInvalidate() {}
- virtual void onResourceError(std::exception_ptr) {}
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
index c9912f0563..8df31f8d7c 100644
--- a/src/mbgl/renderer/render_tile.cpp
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -74,57 +74,75 @@ void RenderTile::finishRender(PaintParameters& parameters) {
static const style::Properties<>::PossiblyEvaluated properties {};
static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
- auto draw = [&] (Color color, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments, auto drawMode) {
+ if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
+ if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
+ tile.debugBucket->complete != tile.isComplete() ||
+ !(tile.debugBucket->modified == tile.modified) ||
+ !(tile.debugBucket->expires == tile.expires) ||
+ tile.debugBucket->debugMode != parameters.debugOptions) {
+ tile.debugBucket = std::make_unique<DebugBucket>(
+ tile.id, tile.isRenderable(), tile.isComplete(), tile.modified,
+ tile.expires, parameters.debugOptions, parameters.context);
+ }
+
parameters.programs.debug.draw(
parameters.context,
- drawMode,
+ gl::Lines { 4.0f * parameters.pixelRatio },
gl::DepthMode::disabled(),
parameters.stencilModeForClipping(clip),
gl::ColorMode::unblended(),
DebugProgram::UniformValues {
uniforms::u_matrix::Value{ matrix },
- uniforms::u_color::Value{ color }
+ uniforms::u_color::Value{ Color::white() }
},
- vertexBuffer,
- indexBuffer,
- segments,
+ *tile.debugBucket->vertexBuffer,
+ *tile.debugBucket->indexBuffer,
+ tile.debugBucket->segments,
paintAttibuteData,
properties,
parameters.state.getZoom(),
"debug"
);
- };
-
- if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
- if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
- tile.debugBucket->complete != tile.isComplete() ||
- !(tile.debugBucket->modified == tile.modified) ||
- !(tile.debugBucket->expires == tile.expires) ||
- tile.debugBucket->debugMode != parameters.debugOptions) {
- tile.debugBucket = std::make_unique<DebugBucket>(
- tile.id, tile.isRenderable(), tile.isComplete(), tile.modified,
- tile.expires, parameters.debugOptions, parameters.context);
- }
- draw(Color::white(),
- *tile.debugBucket->vertexBuffer,
- *tile.debugBucket->indexBuffer,
- tile.debugBucket->segments,
- gl::Lines { 4.0f * parameters.pixelRatio });
-
- draw(Color::black(),
- *tile.debugBucket->vertexBuffer,
- *tile.debugBucket->indexBuffer,
- tile.debugBucket->segments,
- gl::Lines { 2.0f * parameters.pixelRatio });
+ parameters.programs.debug.draw(
+ parameters.context,
+ gl::Lines { 2.0f * parameters.pixelRatio },
+ gl::DepthMode::disabled(),
+ parameters.stencilModeForClipping(clip),
+ gl::ColorMode::unblended(),
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::black() }
+ },
+ *tile.debugBucket->vertexBuffer,
+ *tile.debugBucket->indexBuffer,
+ tile.debugBucket->segments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
}
if (parameters.debugOptions & MapDebugOptions::TileBorders) {
- draw(Color::red(),
- parameters.staticData.tileVertexBuffer,
- parameters.staticData.tileBorderIndexBuffer,
- parameters.staticData.tileBorderSegments,
- gl::LineStrip { 4.0f * parameters.pixelRatio });
+ parameters.programs.debug.draw(
+ parameters.context,
+ gl::LineStrip { 4.0f * parameters.pixelRatio },
+ gl::DepthMode::disabled(),
+ parameters.stencilModeForClipping(clip),
+ gl::ColorMode::unblended(),
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::red() }
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.tileBorderIndexBuffer,
+ parameters.staticData.tileBorderSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
}
}
diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp
index 9f4b897d6e..e915f5e146 100644
--- a/src/mbgl/renderer/renderer.cpp
+++ b/src/mbgl/renderer/renderer.cpp
@@ -1,6 +1,6 @@
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/renderer/renderer_impl.hpp>
-#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
namespace mbgl {
@@ -15,7 +15,14 @@ Renderer::Renderer(RendererBackend& backend,
contextMode_, std::move(programCacheDir_))) {
}
-Renderer::~Renderer() = default;
+Renderer::~Renderer() {
+ BackendScope guard { impl->backend };
+ impl.reset();
+}
+
+void Renderer::markContextLost() {
+ impl->markContextLost();
+}
void Renderer::setObserver(RendererObserver* observer) {
impl->setObserver(observer);
@@ -72,6 +79,7 @@ void Renderer::dumpDebugLogs() {
}
void Renderer::onLowMemory() {
+ BackendScope guard { impl->backend };
impl->onLowMemory();
}
diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp
index dd3c0d41a1..1a828b80a3 100644
--- a/src/mbgl/renderer/renderer_impl.cpp
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -1,13 +1,32 @@
+#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/renderer/renderer_impl.hpp>
-#include <mbgl/renderer/render_style.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/renderer/render_static_data.hpp>
-#include <mbgl/renderer/render_item.hpp>
#include <mbgl/renderer/update_parameters.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/layers/render_background_layer.hpp>
+#include <mbgl/renderer/layers/render_custom_layer.hpp>
+#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
+#include <mbgl/renderer/style_diff.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/gl/debugging.hpp>
#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/logging.hpp>
namespace mbgl {
@@ -24,20 +43,36 @@ Renderer::Impl::Impl(RendererBackend& backend_,
Scheduler& scheduler_,
GLContextMode contextMode_,
const optional<std::string> programCacheDir_)
- : backend(backend_)
- , observer(&nullObserver())
- , contextMode(contextMode_)
- , pixelRatio(pixelRatio_)
- , programCacheDir(programCacheDir_)
- , renderStyle(std::make_unique<RenderStyle>(scheduler_, fileSource_)) {
-
- renderStyle->setObserver(this);
+ : backend(backend_)
+ , scheduler(scheduler_)
+ , fileSource(fileSource_)
+ , observer(&nullObserver())
+ , contextMode(contextMode_)
+ , pixelRatio(pixelRatio_)
+ , programCacheDir(programCacheDir_)
+ , glyphManager(std::make_unique<GlyphManager>(fileSource))
+ , imageManager(std::make_unique<ImageManager>())
+ , lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 }))
+ , imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>())
+ , sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>())
+ , layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>())
+ , renderLight(makeMutable<Light::Impl>()) {
+ glyphManager->setObserver(this);
}
Renderer::Impl::~Impl() {
- BackendScope guard { backend };
- renderStyle.reset();
- staticData.reset();
+ assert(BackendScope::exists());
+
+ if (contextLost) {
+ // Signal all RenderCustomLayers that the context was lost
+ // before cleaning up
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ if (layer.is<RenderCustomLayer>()) {
+ layer.as<RenderCustomLayer>()->markContextDestroyed();
+ }
+ }
+ }
};
void Renderer::Impl::setObserver(RendererObserver* observer_) {
@@ -45,12 +80,164 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) {
}
void Renderer::Impl::render(const UpdateParameters& updateParameters) {
- // Don't load/render anyting in still mode until explicitly requested.
- if (updateParameters.mode == MapMode::Still && !updateParameters.stillImageRequest) return;
+ if (updateParameters.mode == MapMode::Still) {
+ // Don't load/render anyting in still mode until explicitly requested.
+ if (!updateParameters.stillImageRequest) {
+ return;
+ }
+
+ // Reset zoom history state.
+ zoomHistory.first = true;
+ }
assert(BackendScope::exists());
+
+ updateParameters.annotationManager.updateData();
+
+ const bool zoomChanged = zoomHistory.update(updateParameters.transformState.getZoom(), updateParameters.timePoint);
+
+ const TransitionParameters transitionParameters {
+ updateParameters.timePoint,
+ updateParameters.mode == MapMode::Continuous ? updateParameters.transitionOptions : TransitionOptions()
+ };
+
+ const PropertyEvaluationParameters evaluationParameters {
+ zoomHistory,
+ updateParameters.timePoint,
+ updateParameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero()
+ };
+
+ const TileParameters tileParameters {
+ updateParameters.pixelRatio,
+ updateParameters.debugOptions,
+ updateParameters.transformState,
+ scheduler,
+ fileSource,
+ updateParameters.mode,
+ updateParameters.annotationManager,
+ *imageManager,
+ *glyphManager,
+ updateParameters.prefetchZoomDelta
+ };
+
+ glyphManager->setURL(updateParameters.glyphURL);
+
+ // Update light.
+ const bool lightChanged = renderLight.impl != updateParameters.light;
+
+ if (lightChanged) {
+ renderLight.impl = updateParameters.light;
+ renderLight.transition(transitionParameters);
+ }
+
+ if (lightChanged || zoomChanged || renderLight.hasTransition()) {
+ renderLight.evaluate(evaluationParameters);
+ }
+
+
+ const ImageDifference imageDiff = diffImages(imageImpls, updateParameters.images);
+ imageImpls = updateParameters.images;
+
+ // Remove removed images from sprite atlas.
+ for (const auto& entry : imageDiff.removed) {
+ imageManager->removeImage(entry.first);
+ }
+
+ // Add added images to sprite atlas.
+ for (const auto& entry : imageDiff.added) {
+ imageManager->addImage(entry.second);
+ }
+
+ // Update changed images.
+ for (const auto& entry : imageDiff.changed) {
+ imageManager->updateImage(entry.second.after);
+ }
+
+ imageManager->setLoaded(updateParameters.spriteLoaded);
+
+
+ const LayerDifference layerDiff = diffLayers(layerImpls, updateParameters.layers);
+ layerImpls = updateParameters.layers;
+
+ // Remove render layers for removed layers.
+ for (const auto& entry : layerDiff.removed) {
+ renderLayers.erase(entry.first);
+ }
+
+ // Create render layers for newly added layers.
+ for (const auto& entry : layerDiff.added) {
+ renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
+ }
+
+ // Update render layers for changed layers.
+ for (const auto& entry : layerDiff.changed) {
+ renderLayers.at(entry.first)->setImpl(entry.second.after);
+ }
+
+ // Update layers for class and zoom changes.
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ const bool layerAdded = layerDiff.added.count(entry.first);
+ const bool layerChanged = layerDiff.changed.count(entry.first);
+
+ if (layerAdded || layerChanged) {
+ layer.transition(transitionParameters);
+ }
+
+ if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
+ layer.evaluate(evaluationParameters);
+ }
+ }
+
+
+ const SourceDifference sourceDiff = diffSources(sourceImpls, updateParameters.sources);
+ sourceImpls = updateParameters.sources;
+
+ // Remove render layers for removed sources.
+ for (const auto& entry : sourceDiff.removed) {
+ renderSources.erase(entry.first);
+ }
+
+ // Create render sources for newly added sources.
+ for (const auto& entry : sourceDiff.added) {
+ std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
+ renderSource->setObserver(this);
+ renderSources.emplace(entry.first, std::move(renderSource));
+ }
+
+ const bool hasImageDiff = !(imageDiff.added.empty() && imageDiff.removed.empty() && imageDiff.changed.empty());
+
+ // Update all sources.
+ for (const auto& source : *sourceImpls) {
+ std::vector<Immutable<Layer::Impl>> filteredLayers;
+ bool needsRendering = false;
+ bool needsRelayout = false;
+
+ for (const auto& layer : *layerImpls) {
+ if (layer->type == LayerType::Background ||
+ layer->type == LayerType::Custom ||
+ layer->source != source->id) {
+ continue;
+ }
+
+ if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
+ needsRendering = true;
+ }
+
+ if (!needsRelayout && (hasImageDiff || hasLayoutDifference(layerDiff, layer->id))) {
+ needsRelayout = true;
+ }
+
+ filteredLayers.push_back(layer);
+ }
+
+ renderSources.at(source->id)->update(source,
+ filteredLayers,
+ needsRendering,
+ needsRelayout,
+ tileParameters);
+ }
- renderStyle->update(updateParameters);
transformState = updateParameters.transformState;
if (!staticData) {
@@ -63,60 +250,131 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
contextMode,
backend,
updateParameters,
- *renderStyle,
+ renderLight.getEvaluated(),
*staticData,
- frameHistory
+ frameHistory,
+ *imageManager,
+ *lineAtlas
};
- bool loaded = updateParameters.styleLoaded && renderStyle->isLoaded();
+ bool loaded = updateParameters.styleLoaded && isLoaded();
+ if (updateParameters.mode == MapMode::Still && !loaded) {
+ return;
+ }
- if (updateParameters.mode == MapMode::Continuous) {
- if (renderState == RenderState::Never) {
- observer->onWillStartRenderingMap();
- }
+ if (renderState == RenderState::Never) {
+ observer->onWillStartRenderingMap();
+ }
+
+ observer->onWillStartRenderingFrame();
+
+ backend.updateAssumedState();
- observer->onWillStartRenderingFrame();
+ if (parameters.contextMode == GLContextMode::Shared) {
+ parameters.context.setDirtyState();
+ }
- backend.updateAssumedState();
+ Color backgroundColor;
- doRender(parameters);
- parameters.context.performCleanup();
+ class RenderItem {
+ public:
+ RenderLayer& layer;
+ RenderSource* source;
+ };
+
+ std::vector<RenderItem> order;
- observer->onDidFinishRenderingFrame(
- loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
- renderStyle->hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION)
- );
+ for (auto& layerImpl : *layerImpls) {
+ RenderLayer* layer = getRenderLayer(layerImpl->id);
+ assert(layer);
- if (!loaded) {
- renderState = RenderState::Partial;
- } else if (renderState != RenderState::Fully) {
- renderState = RenderState::Fully;
- observer->onDidFinishRenderingMap();
+ if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) {
+ parameters.staticData.has3D = true;
}
- } else if (loaded) {
- observer->onWillStartRenderingMap();
- observer->onWillStartRenderingFrame();
- backend.updateAssumedState();
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
- doRender(parameters);
+ if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
+ const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
+ if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) {
+ // This is a solid background. We can use glClear().
+ backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
+ } else {
+ // This is a textured background, or not the bottommost layer. We need to render it with a quad.
+ order.emplace_back(RenderItem { *layer, nullptr });
+ }
+ continue;
+ }
- observer->onDidFinishRenderingFrame(RendererObserver::RenderMode::Full, false);
- observer->onDidFinishRenderingMap();
+ if (layer->is<RenderCustomLayer>()) {
+ order.emplace_back(RenderItem { *layer, nullptr });
+ continue;
+ }
- // Cleanup only after signaling completion
- parameters.context.performCleanup();
- }
-}
+ RenderSource* source = getRenderSource(layer->baseImpl->source);
+ if (!source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
+ continue;
+ }
-void Renderer::Impl::doRender(PaintParameters& parameters) {
- if (parameters.contextMode == GLContextMode::Shared) {
- parameters.context.setDirtyState();
- }
+ const bool symbolLayer = layer->is<RenderSymbolLayer>();
+
+ auto sortedTiles = source->getRenderTiles();
+ if (symbolLayer) {
+ // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn
+ // on top of each other, with lower symbols being drawn on top of higher symbols.
+ std::sort(sortedTiles.begin(), sortedTiles.end(), [&](const RenderTile& a, const RenderTile& b) {
+ Point<float> pa(a.id.canonical.x, a.id.canonical.y);
+ Point<float> pb(b.id.canonical.x, b.id.canonical.y);
- RenderData renderData = renderStyle->getRenderData(parameters.debugOptions, parameters.state.getAngle());
- const std::vector<RenderItem>& order = renderData.order;
- const std::unordered_set<RenderSource*>& sources = renderData.sources;
+ auto par = util::rotate(pa, parameters.state.getAngle());
+ auto pbr = util::rotate(pb, parameters.state.getAngle());
+
+ return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x);
+ });
+ } else {
+ std::sort(sortedTiles.begin(), sortedTiles.end(),
+ [](const auto& a, const auto& b) { return a.get().id < b.get().id; });
+ }
+
+ std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
+ for (auto& sortedTile : sortedTiles) {
+ auto& tile = sortedTile.get();
+ if (!tile.tile.isRenderable()) {
+ continue;
+ }
+
+ // We're not clipping symbol layers, so when we have both parents and children of symbol
+ // layers, we drop all children in favor of their parent to avoid duplicate labels.
+ // See https://github.com/mapbox/mapbox-gl-native/issues/2482
+ if (symbolLayer) {
+ bool skip = false;
+ // Look back through the buckets we decided to render to find out whether there is
+ // already a bucket from this layer that is a parent of this tile. Tiles are ordered
+ // by zoom level when we obtain them from getTiles().
+ for (auto it = sortedTilesForInsertion.rbegin();
+ it != sortedTilesForInsertion.rend(); ++it) {
+ if (tile.tile.id.isChildOf(it->get().tile.id)) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ continue;
+ }
+ }
+
+ auto bucket = tile.tile.getBucket(*layer->baseImpl);
+ if (bucket) {
+ sortedTilesForInsertion.emplace_back(tile);
+ tile.used = true;
+ }
+ }
+ layer->setRenderTiles(std::move(sortedTilesForInsertion));
+ order.emplace_back(RenderItem { *layer, source });
+ }
frameHistory.record(parameters.timePoint,
parameters.state.getZoom(),
@@ -130,6 +388,39 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
parameters.imageManager.upload(parameters.context, 0);
parameters.lineAtlas.upload(parameters.context, 0);
parameters.frameHistory.upload(parameters.context, 0);
+
+ // Update all clipping IDs + upload buckets.
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ entry.second->startRender(parameters);
+ }
+ }
+ }
+
+ // - 3D PASS -------------------------------------------------------------------------------------
+ // Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same
+ // depth rbo between them.
+ if (parameters.staticData.has3D) {
+ parameters.staticData.backendSize = parameters.backend.getFramebufferSize();
+
+ MBGL_DEBUG_GROUP(parameters.context, "3d");
+ parameters.pass = RenderPass::Pass3D;
+
+ if (!parameters.staticData.depthRenderbuffer ||
+ parameters.staticData.depthRenderbuffer->size != parameters.staticData.backendSize) {
+ parameters.staticData.depthRenderbuffer =
+ parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(parameters.staticData.backendSize);
+ }
+ parameters.staticData.depthRenderbuffer->shouldClear(true);
+
+ uint32_t i = static_cast<uint32_t>(order.size()) - 1;
+ for (auto it = order.begin(); it != order.end(); ++it, --i) {
+ parameters.currentLayer = i;
+ if (it->layer.hasRenderPass(parameters.pass)) {
+ MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
+ it->layer.render(parameters, it->source);
+ }
+ }
}
// - CLEAR -------------------------------------------------------------------------------------
@@ -140,7 +431,7 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
parameters.backend.bind();
parameters.context.clear((parameters.debugOptions & MapDebugOptions::Overdraw)
? Color::black()
- : renderData.backgroundColor,
+ : backgroundColor,
1.0f,
0);
}
@@ -148,13 +439,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
// - CLIPPING MASKS ----------------------------------------------------------------------------
// Draws the clipping masks to the stencil buffer.
{
- MBGL_DEBUG_GROUP(parameters.context, "clip");
-
- // Update all clipping IDs.
- for (const auto& source : sources) {
- source->startRender(parameters);
- }
-
MBGL_DEBUG_GROUP(parameters.context, "clipping masks");
static const style::FillPaintProperties::PossiblyEvaluated properties {};
@@ -220,10 +504,7 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
}
#endif
- int indent = 0;
-
// Actually render the layers
- if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
parameters.depthRangeSize = 1 - (order.size() + 2) * parameters.numSublayers * parameters.depthEpsilon;
@@ -233,10 +514,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
parameters.pass = RenderPass::Opaque;
MBGL_DEBUG_GROUP(parameters.context, "opaque");
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", "opaque");
- }
-
uint32_t i = 0;
for (auto it = order.rbegin(); it != order.rend(); ++it, ++i) {
parameters.currentLayer = i;
@@ -245,10 +522,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
it->layer.render(parameters, it->source);
}
}
-
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
- }
}
// - TRANSLUCENT PASS --------------------------------------------------------------------------
@@ -257,10 +530,6 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
parameters.pass = RenderPass::Translucent;
MBGL_DEBUG_GROUP(parameters.context, "translucent");
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", "translucent");
- }
-
uint32_t i = static_cast<uint32_t>(order.size()) - 1;
for (auto it = order.begin(); it != order.end(); ++it, --i) {
parameters.currentLayer = i;
@@ -269,14 +538,8 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
it->layer.render(parameters, it->source);
}
}
-
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
- }
}
- if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; }
-
// - DEBUG PASS --------------------------------------------------------------------------------
// Renders debug overlays.
{
@@ -286,8 +549,10 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
// This guarantees that we have at least one function per tile called.
// When only rendering layers via the stylesheet, it's possible that we don't
// ever visit a tile during rendering.
- for (const auto& source : sources) {
- source->finishRender(parameters);
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ entry.second->finishRender(parameters);
+ }
}
}
@@ -319,43 +584,159 @@ void Renderer::Impl::doRender(PaintParameters& parameters) {
{
MBGL_DEBUG_GROUP(parameters.context, "cleanup");
- parameters.context.activeTexture = 1;
+ parameters.context.activeTextureUnit = 1;
parameters.context.texture[1] = 0;
- parameters.context.activeTexture = 0;
+ parameters.context.activeTextureUnit = 0;
parameters.context.texture[0] = 0;
parameters.context.bindVertexArray = 0;
}
+
+ observer->onDidFinishRenderingFrame(
+ loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
+ updateParameters.mode == MapMode::Continuous && (hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION))
+ );
+
+ if (!loaded) {
+ renderState = RenderState::Partial;
+ } else if (renderState != RenderState::Fully) {
+ renderState = RenderState::Fully;
+ observer->onDidFinishRenderingMap();
+ }
+
+ // Cleanup only after signaling completion
+ parameters.context.performCleanup();
}
std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const {
- return renderStyle->queryRenderedFeatures(geometry, transformState, options);
+ std::vector<const RenderLayer*> layers;
+ if (options.layerIDs) {
+ for (const auto& layerID : *options.layerIDs) {
+ if (const RenderLayer* layer = getRenderLayer(layerID)) {
+ layers.emplace_back(layer);
+ }
+ }
+ } else {
+ for (const auto& entry : renderLayers) {
+ layers.emplace_back(entry.second.get());
+ }
+ }
+
+ std::unordered_set<std::string> sourceIDs;
+ for (const RenderLayer* layer : layers) {
+ sourceIDs.emplace(layer->baseImpl->source);
+ }
+
+ std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
+ for (const auto& sourceID : sourceIDs) {
+ if (RenderSource* renderSource = getRenderSource(sourceID)) {
+ auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options);
+ std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
+ }
+ }
+
+ std::vector<Feature> result;
+
+ if (resultsByLayer.empty()) {
+ return result;
+ }
+
+ // Combine all results based on the style layer order.
+ for (const auto& layerImpl : *layerImpls) {
+ const RenderLayer* layer = getRenderLayer(layerImpl->id);
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+ auto it = resultsByLayer.find(layer->baseImpl->id);
+ if (it != resultsByLayer.end()) {
+ std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
+ }
+ }
+
+ return result;
}
std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const {
- const RenderSource* source = renderStyle->getRenderSource(sourceID);
+ const RenderSource* source = getRenderSource(sourceID);
if (!source) return {};
return source->querySourceFeatures(options);
}
-void Renderer::Impl::onInvalidate() {
+void Renderer::Impl::onLowMemory() {
+ assert(BackendScope::exists());
+ backend.getContext().performCleanup();
+ for (const auto& entry : renderSources) {
+ entry.second->onLowMemory();
+ }
observer->onInvalidate();
}
-void Renderer::Impl::onResourceError(std::exception_ptr ptr) {
- observer->onResourceError(ptr);
+void Renderer::Impl::dumDebugLogs() {
+ for (const auto& entry : renderSources) {
+ entry.second->dumpDebugLogs();
+ }
+
+ imageManager->dumpDebugLogs();
}
-void Renderer::Impl::onLowMemory() {
- BackendScope guard { backend };
- backend.getContext().performCleanup();
- renderStyle->onLowMemory();
- observer->onInvalidate();
+RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
}
-void Renderer::Impl::dumDebugLogs() {
- renderStyle->dumpDebugLogs();
+const RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) const {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const {
+ auto it = renderSources.find(id);
+ return it != renderSources.end() ? it->second.get() : nullptr;
+}
+
+bool Renderer::Impl::hasTransitions() const {
+ if (renderLight.hasTransition()) {
+ return true;
+ }
+
+ for (const auto& entry : renderLayers) {
+ if (entry.second->hasTransition()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Renderer::Impl::isLoaded() const {
+ for (const auto& entry: renderSources) {
+ if (!entry.second->isLoaded()) {
+ return false;
+ }
+ }
+
+ if (!imageManager->isLoaded()) {
+ return false;
+ }
+
+ return true;
+}
+
+void Renderer::Impl::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
+ glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void Renderer::Impl::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
+ util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void Renderer::Impl::onTileChanged(RenderSource&, const OverscaledTileID&) {
+ observer->onInvalidate();
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp
index 079b00d0bb..30e7f70722 100644
--- a/src/mbgl/renderer/renderer_impl.hpp
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -1,11 +1,16 @@
#pragma once
#include <mbgl/renderer/renderer.hpp>
-#include <mbgl/renderer/renderer_backend.hpp>
-#include <mbgl/renderer/renderer_observer.hpp>
-#include <mbgl/renderer/render_style_observer.hpp>
+#include <mbgl/renderer/render_source_observer.hpp>
+#include <mbgl/renderer/render_light.hpp>
#include <mbgl/renderer/frame_history.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/source.hpp>
+#include <mbgl/style/layer.hpp>
#include <mbgl/map/transform_state.hpp>
+#include <mbgl/map/zoom_history.hpp>
+#include <mbgl/map/mode.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
#include <memory>
#include <string>
@@ -13,17 +18,31 @@
namespace mbgl {
+class RendererBackend;
+class RendererObserver;
+class RenderSource;
+class RenderLayer;
class UpdateParameters;
-class PaintParameters;
-class RenderStyle;
class RenderStaticData;
-
-class Renderer::Impl : public RenderStyleObserver {
+class RenderedQueryOptions;
+class SourceQueryOptions;
+class FileSource;
+class Scheduler;
+class GlyphManager;
+class ImageManager;
+class LineAtlas;
+
+class Renderer::Impl : public GlyphManagerObserver,
+ public RenderSourceObserver{
public:
Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode,
const optional<std::string> programCacheDir);
~Impl() final;
+ void markContextLost() {
+ contextLost = true;
+ };
+
void setObserver(RendererObserver*);
void render(const UpdateParameters&);
@@ -34,16 +53,28 @@ public:
void onLowMemory();
void dumDebugLogs();
- // RenderStyleObserver implementation
- void onInvalidate() override;
- void onResourceError(std::exception_ptr) override;
-
private:
- void doRender(PaintParameters&);
+ bool isLoaded() const;
+ bool hasTransitions() const;
+
+ RenderSource* getRenderSource(const std::string& id) const;
+
+ RenderLayer* getRenderLayer(const std::string& id);
+ const RenderLayer* getRenderLayer(const std::string& id) const;
+
+ // GlyphManagerObserver implementation.
+ void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
+
+ // RenderSourceObserver implementation.
+ void onTileChanged(RenderSource&, const OverscaledTileID&) override;
+ void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
friend class Renderer;
RendererBackend& backend;
+ Scheduler& scheduler;
+ FileSource& fileSource;
+
RendererObserver* observer;
const GLContextMode contextMode;
@@ -58,10 +89,23 @@ private:
RenderState renderState = RenderState::Never;
FrameHistory frameHistory;
+ ZoomHistory zoomHistory;
TransformState transformState;
- std::unique_ptr<RenderStyle> renderStyle;
+ std::unique_ptr<GlyphManager> glyphManager;
+ std::unique_ptr<ImageManager> imageManager;
+ std::unique_ptr<LineAtlas> lineAtlas;
std::unique_ptr<RenderStaticData> staticData;
+
+ Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;
+ Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls;
+ Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls;
+
+ std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources;
+ std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
+ RenderLight renderLight;
+
+ bool contextLost = false;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp
index df8bcc0ae7..504db78ea3 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -2,6 +2,7 @@
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/tile/geojson_tile.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
@@ -34,19 +35,26 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
GeoJSONData* data_ = impl().getData();
- if (!data_) {
- return;
- }
-
if (data_ != data) {
data = data_;
tilePyramid.cache.clear();
- for (auto const& item : tilePyramid.tiles) {
- static_cast<GeoJSONTile*>(item.second.get())->updateData(data->getTile(item.first.canonical));
+ if (data) {
+ const uint8_t maxZ = impl().getZoomRange().max;
+ for (const auto& pair : tilePyramid.tiles) {
+ if (pair.first.canonical.z <= maxZ) {
+ static_cast<GeoJSONTile*>(pair.second.get())->updateData(data->getTile(pair.first.canonical));
+ }
+ }
}
}
+ if (!data) {
+ tilePyramid.tiles.clear();
+ tilePyramid.renderTiles.clear();
+ return;
+ }
+
tilePyramid.update(layers,
needsRendering,
needsRelayout,
@@ -75,9 +83,9 @@ std::vector<std::reference_wrapper<RenderTile>> RenderGeoJSONSource::getRenderTi
std::unordered_map<std::string, std::vector<Feature>>
RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
}
std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const {
diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp
index b84156ab95..72ab4879ef 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.hpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.hpp
@@ -30,7 +30,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp
index 11ff8c26b1..9140e01711 100644
--- a/src/mbgl/renderer/sources/render_image_source.cpp
+++ b/src/mbgl/renderer/sources/render_image_source.cpp
@@ -10,6 +10,7 @@
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/logging.hpp>
+#include <mbgl/util/constants.hpp>
namespace mbgl {
@@ -82,7 +83,7 @@ void RenderImageSource::finishRender(PaintParameters& parameters) {
std::unordered_map<std::string, std::vector<Feature>>
RenderImageSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
@@ -193,11 +194,11 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
bucket->vertices.emplace_back(
RasterProgram::layoutVertex({ geomCoords[0].x, geomCoords[0].y }, { 0, 0 }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { 32767, 0 }));
+ RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, 32767 }));
+ RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { 32767, 32767 }));
+ RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { util::EXTENT, util::EXTENT }));
bucket->indices.emplace_back(0, 1, 2);
bucket->indices.emplace_back(1, 2, 3);
diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp
index fc1a462090..7b69d09fa7 100644
--- a/src/mbgl/renderer/sources/render_image_source.hpp
+++ b/src/mbgl/renderer/sources/render_image_source.hpp
@@ -31,7 +31,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final;
diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp
index cbd874f647..bcd719365d 100644
--- a/src/mbgl/renderer/sources/render_raster_source.cpp
+++ b/src/mbgl/renderer/sources/render_raster_source.cpp
@@ -73,7 +73,7 @@ std::vector<std::reference_wrapper<RenderTile>> RenderRasterSource::getRenderTil
std::unordered_map<std::string, std::vector<Feature>>
RenderRasterSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&) const {
return std::unordered_map<std::string, std::vector<Feature>> {};
}
diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp
index 1f4678da9f..01de812309 100644
--- a/src/mbgl/renderer/sources/render_raster_source.hpp
+++ b/src/mbgl/renderer/sources/render_raster_source.hpp
@@ -26,7 +26,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp
index 47bccdaca8..ca3071c6b0 100644
--- a/src/mbgl/renderer/sources/render_vector_source.cpp
+++ b/src/mbgl/renderer/sources/render_vector_source.cpp
@@ -76,9 +76,9 @@ std::vector<std::reference_wrapper<RenderTile>> RenderVectorSource::getRenderTil
std::unordered_map<std::string, std::vector<Feature>>
RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options);
}
std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const {
diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp
index 256ad4e800..5e5c6d1108 100644
--- a/src/mbgl/renderer/sources/render_vector_source.hpp
+++ b/src/mbgl/renderer/sources/render_vector_source.hpp
@@ -26,7 +26,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const final;
std::vector<Feature>
diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp
index 219f154675..6cd9bd9ebd 100644
--- a/src/mbgl/renderer/tile_pyramid.cpp
+++ b/src/mbgl/renderer/tile_pyramid.cpp
@@ -171,7 +171,26 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
cache.setSize(conservativeCacheSize);
}
- removeStaleTiles(retain);
+ // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep
+ // and removes items from tiles that don't have the corresponding key in the retain set.
+ {
+ auto tilesIt = tiles.begin();
+ auto retainIt = retain.begin();
+ while (tilesIt != tiles.end()) {
+ if (retainIt == retain.end() || tilesIt->first < *retainIt) {
+ if (!needsRelayout) {
+ tilesIt->second->setNecessity(Tile::Necessity::Optional);
+ cache.add(tilesIt->first, std::move(tilesIt->second));
+ }
+ tiles.erase(tilesIt++);
+ } else {
+ if (!(*retainIt < tilesIt->first)) {
+ ++tilesIt;
+ }
+ ++retainIt;
+ }
+ }
+ }
for (auto& pair : tiles) {
const PlacementConfig config { parameters.transformState.getAngle(),
@@ -184,29 +203,9 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
}
}
-// Moves all tiles to the cache except for those specified in the retain set.
-void TilePyramid::removeStaleTiles(const std::set<OverscaledTileID>& retain) {
- // Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep
- // and removes items from tiles that don't have the corresponding key in the retain set.
- auto tilesIt = tiles.begin();
- auto retainIt = retain.begin();
- while (tilesIt != tiles.end()) {
- if (retainIt == retain.end() || tilesIt->first < *retainIt) {
- tilesIt->second->setNecessity(Tile::Necessity::Optional);
- cache.add(tilesIt->first, std::move(tilesIt->second));
- tiles.erase(tilesIt++);
- } else {
- if (!(*retainIt < tilesIt->first)) {
- ++tilesIt;
- }
- ++retainIt;
- }
- }
-}
-
std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
@@ -249,7 +248,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
renderTile.tile.queryRenderedFeatures(result,
tileSpaceQueryGeometry,
transformState,
- style,
+ layers,
options);
}
diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp
index d940f378fa..73a8d34c1c 100644
--- a/src/mbgl/renderer/tile_pyramid.hpp
+++ b/src/mbgl/renderer/tile_pyramid.hpp
@@ -21,7 +21,7 @@ namespace mbgl {
class PaintParameters;
class TransformState;
class RenderTile;
-class RenderStyle;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
class TileParameters;
@@ -50,7 +50,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>&,
const RenderedQueryOptions& options) const;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const;
@@ -63,8 +63,6 @@ public:
bool enabled = false;
- void removeStaleTiles(const std::set<OverscaledTileID>&);
-
std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
TileCache cache;
diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp
index 181b11784d..b54abc050d 100644
--- a/src/mbgl/renderer/update_parameters.hpp
+++ b/src/mbgl/renderer/update_parameters.hpp
@@ -13,8 +13,6 @@
namespace mbgl {
-class Scheduler;
-class FileSource;
class AnnotationManager;
class UpdateParameters {
@@ -34,8 +32,6 @@ public:
const Immutable<std::vector<Immutable<style::Source::Impl>>> sources;
const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers;
- Scheduler& scheduler;
- FileSource& fileSource;
AnnotationManager& annotationManager;
const uint8_t prefetchZoomDelta;
diff --git a/src/mbgl/shaders/circle.cpp b/src/mbgl/shaders/circle.cpp
index 953e750776..c14335914b 100644
--- a/src/mbgl/shaders/circle.cpp
+++ b/src/mbgl/shaders/circle.cpp
@@ -24,6 +24,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
uniform lowp float a_radius_t;
attribute mediump vec2 a_radius;
@@ -32,6 +33,7 @@ varying mediump float radius;
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -40,6 +42,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -48,6 +51,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
uniform lowp float a_stroke_color_t;
attribute highp vec4 a_stroke_color;
@@ -56,6 +60,7 @@ varying highp vec4 stroke_color;
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
uniform lowp float a_stroke_width_t;
attribute mediump vec2 a_stroke_width;
@@ -64,6 +69,7 @@ varying mediump float stroke_width;
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
uniform lowp float a_stroke_opacity_t;
attribute lowp vec2 a_stroke_opacity;
@@ -72,52 +78,60 @@ varying lowp float stroke_opacity;
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main(void) {
-
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
radius = unpack_mix_vec2(a_radius, a_radius_t);
#else
mediump float radius = u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
stroke_color = unpack_mix_vec4(a_stroke_color, a_stroke_color_t);
#else
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
stroke_width = unpack_mix_vec2(a_stroke_width, a_stroke_width_t);
#else
mediump float stroke_width = u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
stroke_opacity = unpack_mix_vec2(a_stroke_opacity, a_stroke_opacity_t);
#else
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
// unencode the extrusion vector that we snuck into the a_pos vector
vec2 extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);
@@ -164,74 +178,88 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
varying mediump float radius;
#else
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
varying highp vec4 stroke_color;
#else
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
varying mediump float stroke_width;
#else
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
varying lowp float stroke_opacity;
#else
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main() {
-
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_radius
mediump float radius = u_radius;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_color
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_width
mediump float stroke_width = u_stroke_width;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_opacity
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
vec2 extrude = v_data.xy;
float extrude_length = length(extrude);
diff --git a/src/mbgl/shaders/debug.cpp b/src/mbgl/shaders/debug.cpp
index d39dcf25be..d18f3be5d1 100644
--- a/src/mbgl/shaders/debug.cpp
+++ b/src/mbgl/shaders/debug.cpp
@@ -12,7 +12,12 @@ attribute vec2 a_pos;
uniform mat4 u_matrix;
void main() {
- gl_Position = u_matrix * vec4(a_pos, step(32767.0, a_pos.x), 1);
+ // We are using Int16 for texture position coordinates to give us enough precision for
+ // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer
+ // as an arbitrarily high number to preserve adequate precision when rendering.
+ // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates,
+ // so math for modifying either is consistent.
+ gl_Position = u_matrix * vec4(a_pos, step(8192.0, a_pos.x), 1);
}
)MBGL_SHADER";
diff --git a/src/mbgl/shaders/fill.cpp b/src/mbgl/shaders/fill.cpp
index 8f5f304014..3ba00836a2 100644
--- a/src/mbgl/shaders/fill.cpp
+++ b/src/mbgl/shaders/fill.cpp
@@ -20,6 +20,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -28,20 +29,23 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
}
@@ -54,22 +58,26 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
gl_FragColor = color * opacity;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/fill_extrusion.cpp b/src/mbgl/shaders/fill_extrusion.cpp
index ad14e4f32e..817f73391c 100644
--- a/src/mbgl/shaders/fill_extrusion.cpp
+++ b/src/mbgl/shaders/fill_extrusion.cpp
@@ -27,6 +27,7 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
uniform lowp float a_height_t;
attribute lowp vec2 a_height;
@@ -36,6 +37,7 @@ uniform lowp float u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
uniform lowp float a_color_t;
attribute highp vec4 a_color;
@@ -44,26 +46,30 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_base
base = unpack_mix_vec2(a_base, a_base_t);
#else
lowp float base = u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
height = unpack_mix_vec2(a_height, a_height_t);
#else
lowp float height = u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
base = max(0.0, base);
height = max(0.0, height);
@@ -113,32 +119,38 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
varying lowp float height;
#else
uniform lowp float u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
varying highp vec4 color;
#else
uniform highp vec4 u_color;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_base
lowp float base = u_base;
#endif
+
#ifdef HAS_UNIFORM_u_height
lowp float height = u_height;
#endif
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
gl_FragColor = v_color;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/fill_extrusion_pattern.cpp b/src/mbgl/shaders/fill_extrusion_pattern.cpp
index 2681973af6..d3e5eef1bf 100644
--- a/src/mbgl/shaders/fill_extrusion_pattern.cpp
+++ b/src/mbgl/shaders/fill_extrusion_pattern.cpp
@@ -39,6 +39,7 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
uniform lowp float a_height_t;
attribute lowp vec2 a_height;
@@ -47,20 +48,23 @@ varying lowp float height;
uniform lowp float u_height;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_base
base = unpack_mix_vec2(a_base, a_base_t);
#else
lowp float base = u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
height = unpack_mix_vec2(a_height, a_height_t);
#else
lowp float height = u_height;
#endif
+
base = max(0.0, base);
height = max(0.0, height);
@@ -109,22 +113,26 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
varying lowp float height;
#else
uniform lowp float u_height;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_base
lowp float base = u_base;
#endif
+
#ifdef HAS_UNIFORM_u_height
lowp float height = u_height;
#endif
+
vec2 imagecoord = mod(v_pos_a, 1.0);
vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
diff --git a/src/mbgl/shaders/fill_outline.cpp b/src/mbgl/shaders/fill_outline.cpp
index 18a4d8c0a8..9ade598d10 100644
--- a/src/mbgl/shaders/fill_outline.cpp
+++ b/src/mbgl/shaders/fill_outline.cpp
@@ -23,6 +23,7 @@ varying highp vec4 outline_color;
uniform highp vec4 u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -31,20 +32,23 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_outline_color
outline_color = unpack_mix_vec4(a_outline_color, a_outline_color_t);
#else
highp vec4 outline_color = u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;
}
@@ -58,24 +62,28 @@ varying highp vec4 outline_color;
uniform highp vec4 u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
varying vec2 v_pos;
void main() {
-
+
#ifdef HAS_UNIFORM_u_outline_color
highp vec4 outline_color = u_outline_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
float dist = length(v_pos - gl_FragCoord.xy);
float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
gl_FragColor = outline_color * (alpha * opacity);
diff --git a/src/mbgl/shaders/fill_outline_pattern.cpp b/src/mbgl/shaders/fill_outline_pattern.cpp
index 68e69c2135..11cddb7d07 100644
--- a/src/mbgl/shaders/fill_outline_pattern.cpp
+++ b/src/mbgl/shaders/fill_outline_pattern.cpp
@@ -32,14 +32,16 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos);
@@ -70,12 +72,14 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
vec2 imagecoord = mod(v_pos_a, 1.0);
vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
diff --git a/src/mbgl/shaders/fill_pattern.cpp b/src/mbgl/shaders/fill_pattern.cpp
index f6f9e2fbff..a3817c4426 100644
--- a/src/mbgl/shaders/fill_pattern.cpp
+++ b/src/mbgl/shaders/fill_pattern.cpp
@@ -30,14 +30,16 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos);
@@ -65,12 +67,14 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
vec2 imagecoord = mod(v_pos_a, 1.0);
vec2 pos = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, imagecoord);
vec4 color1 = texture2D(u_image, pos);
diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp
index 1eb92c4b71..c700295a15 100644
--- a/src/mbgl/shaders/line.cpp
+++ b/src/mbgl/shaders/line.cpp
@@ -21,7 +21,7 @@ const char* line::vertexSource = R"MBGL_SHADER(
// #define scale 63.0
#define scale 0.015873016
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -41,6 +41,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -49,6 +50,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -57,6 +59,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -64,6 +67,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -71,6 +75,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -78,61 +83,66 @@ attribute mediump vec2 a_width;
uniform mediump float u_width;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
mediump float width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
-
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
- offset = -1.0 * offset;
+ offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -149,9 +159,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -172,36 +179,42 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
varying vec2 v_width2;
varying vec2 v_normal;
varying float v_gamma_scale;
void main() {
-
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp
index 222042a13c..f8d785ade9 100644
--- a/src/mbgl/shaders/line_pattern.cpp
+++ b/src/mbgl/shaders/line_pattern.cpp
@@ -23,7 +23,7 @@ const char* line_pattern::vertexSource = R"MBGL_SHADER(
// Retina devices need a smaller distance to avoid aliasing.
#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -44,6 +44,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -52,6 +53,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -59,6 +61,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -66,6 +69,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -73,55 +77,60 @@ attribute mediump vec2 a_width;
uniform mediump float u_width;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_width
mediump float width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
- offset = -1.0 * offset;
+ offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -138,9 +147,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -178,22 +184,26 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/line_sdf.cpp b/src/mbgl/shaders/line_sdf.cpp
index 168f4ca98d..c5d50566e8 100644
--- a/src/mbgl/shaders/line_sdf.cpp
+++ b/src/mbgl/shaders/line_sdf.cpp
@@ -23,7 +23,7 @@ const char* line_sdf::vertexSource = R"MBGL_SHADER(
// Retina devices need a smaller distance to avoid aliasing.
#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -49,6 +49,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -57,6 +58,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -65,6 +67,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -72,6 +75,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -79,6 +83,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -87,6 +92,7 @@ varying mediump float width;
uniform mediump float u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
uniform lowp float a_floorwidth_t;
attribute lowp vec2 a_floorwidth;
@@ -95,68 +101,75 @@ varying lowp float floorwidth;
uniform lowp float u_floorwidth;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
floorwidth = unpack_mix_vec2(a_floorwidth, a_floorwidth_t);
#else
lowp float floorwidth = u_floorwidth;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
offset = -1.0 * offset;
-
+
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -172,9 +185,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -209,52 +219,62 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_width
varying mediump float width;
#else
uniform mediump float u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
varying lowp float floorwidth;
#else
uniform lowp float u_floorwidth;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_width
mediump float width = u_width;
#endif
+
#ifdef HAS_UNIFORM_u_floorwidth
lowp float floorwidth = u_floorwidth;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/raster.cpp b/src/mbgl/shaders/raster.cpp
index f454078310..98291bfec6 100644
--- a/src/mbgl/shaders/raster.cpp
+++ b/src/mbgl/shaders/raster.cpp
@@ -20,7 +20,12 @@ varying vec2 v_pos1;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
- v_pos0 = (((a_texture_pos / 32767.0) - 0.5) / u_buffer_scale ) + 0.5;
+ // We are using Int16 for texture position coordinates to give us enough precision for
+ // fractional coordinates. We use 8192 to scale the texture coordinates in the buffer
+ // as an arbitrarily high number to preserve adequate precision when rendering.
+ // This is also the same value as the EXTENT we are using for our tile buffer pos coordinates,
+ // so math for modifying either is consistent.
+ v_pos0 = (((a_texture_pos / 8192.0) - 0.5) / u_buffer_scale ) + 0.5;
v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent;
}
diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp
index c0e3a0ac01..1e96194738 100644
--- a/src/mbgl/shaders/symbol_icon.cpp
+++ b/src/mbgl/shaders/symbol_icon.cpp
@@ -32,6 +32,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
uniform mat4 u_matrix;
uniform mat4 u_label_plane_matrix;
uniform mat4 u_gl_coord_matrix;
@@ -45,13 +46,14 @@ varying vec2 v_tex;
varying vec2 v_fade_tex;
void main() {
-
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
@@ -125,15 +127,17 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
varying vec2 v_tex;
varying vec2 v_fade_tex;
void main() {
-
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity;
gl_FragColor = texture2D(u_texture, v_tex) * alpha;
diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp
index 2050886957..a4427f31ab 100644
--- a/src/mbgl/shaders/symbol_sdf.cpp
+++ b/src/mbgl/shaders/symbol_sdf.cpp
@@ -34,6 +34,7 @@ varying highp vec4 fill_color;
uniform highp vec4 u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
uniform lowp float a_halo_color_t;
attribute highp vec4 a_halo_color;
@@ -42,6 +43,7 @@ varying highp vec4 halo_color;
uniform highp vec4 u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -50,6 +52,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
uniform lowp float a_halo_width_t;
attribute lowp vec2 a_halo_width;
@@ -58,6 +61,7 @@ varying lowp float halo_width;
uniform lowp float u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
uniform lowp float a_halo_blur_t;
attribute lowp vec2 a_halo_blur;
@@ -66,6 +70,7 @@ varying lowp float halo_blur;
uniform lowp float u_halo_blur;
#endif
+
uniform mat4 u_matrix;
uniform mat4 u_label_plane_matrix;
uniform mat4 u_gl_coord_matrix;
@@ -84,37 +89,42 @@ varying vec4 v_data0;
varying vec2 v_data1;
void main() {
-
+
#ifndef HAS_UNIFORM_u_fill_color
fill_color = unpack_mix_vec4(a_fill_color, a_fill_color_t);
#else
highp vec4 fill_color = u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
halo_color = unpack_mix_vec4(a_halo_color, a_halo_color_t);
#else
highp vec4 halo_color = u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
halo_width = unpack_mix_vec2(a_halo_width, a_halo_width_t);
#else
lowp float halo_width = u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
halo_blur = unpack_mix_vec2(a_halo_blur, a_halo_blur_t);
#else
lowp float halo_blur = u_halo_blur;
#endif
+
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
@@ -215,30 +225,35 @@ varying highp vec4 fill_color;
uniform highp vec4 u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
varying highp vec4 halo_color;
#else
uniform highp vec4 u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
varying lowp float halo_width;
#else
uniform lowp float u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
varying lowp float halo_blur;
#else
uniform lowp float u_halo_blur;
#endif
+
uniform sampler2D u_texture;
uniform sampler2D u_fadetexture;
uniform highp float u_gamma_scale;
@@ -248,27 +263,32 @@ varying vec4 v_data0;
varying vec2 v_data1;
void main() {
-
+
#ifdef HAS_UNIFORM_u_fill_color
highp vec4 fill_color = u_fill_color;
#endif
+
#ifdef HAS_UNIFORM_u_halo_color
highp vec4 halo_color = u_halo_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_halo_width
lowp float halo_width = u_halo_width;
#endif
+
#ifdef HAS_UNIFORM_u_halo_blur
lowp float halo_blur = u_halo_blur;
#endif
+
vec2 tex = v_data0.xy;
vec2 fade_tex = v_data0.zw;
float gamma_scale = v_data1.x;
diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp
index 60ece5ed73..93d6dfd9ae 100644
--- a/src/mbgl/sprite/sprite_loader.cpp
+++ b/src/mbgl/sprite/sprite_loader.cpp
@@ -10,8 +10,8 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <cassert>
@@ -21,7 +21,7 @@ static SpriteLoaderObserver nullObserver;
struct SpriteLoader::Loader {
Loader(Scheduler& scheduler, SpriteLoader& imageManager)
- : mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ : mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(scheduler, ActorRef<SpriteLoader>(imageManager, mailbox)) {
}
diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp
index a174339074..222f55db84 100644
--- a/src/mbgl/storage/response.cpp
+++ b/src/mbgl/storage/response.cpp
@@ -14,6 +14,7 @@ Response& Response::operator=(const Response& res) {
error = res.error ? std::make_unique<Error>(*res.error) : nullptr;
noContent = res.noContent;
notModified = res.notModified;
+ mustRevalidate = res.mustRevalidate;
data = res.data;
modified = res.modified;
expires = res.expires;
diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp
index 1a30a1f1c7..dd179f5376 100644
--- a/src/mbgl/style/function/categorical_stops.cpp
+++ b/src/mbgl/style/function/categorical_stops.cpp
@@ -34,7 +34,7 @@ template class CategoricalStops<std::array<float, 2>>;
template class CategoricalStops<std::string>;
template class CategoricalStops<TextTransformType>;
template class CategoricalStops<TextJustifyType>;
-template class CategoricalStops<TextAnchorType>;
+template class CategoricalStops<SymbolAnchorType>;
template class CategoricalStops<LineJoinType>;
} // namespace style
diff --git a/src/mbgl/style/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp
index 7815f4aca0..0ac6fda846 100644
--- a/src/mbgl/style/function/identity_stops.cpp
+++ b/src/mbgl/style/function/identity_stops.cpp
@@ -50,12 +50,12 @@ optional<TextJustifyType> IdentityStops<TextJustifyType>::evaluate(const Value&
}
template <>
-optional<TextAnchorType> IdentityStops<TextAnchorType>::evaluate(const Value& value) const {
+optional<SymbolAnchorType> IdentityStops<SymbolAnchorType>::evaluate(const Value& value) const {
if (!value.is<std::string>()) {
return {};
}
- return Enum<TextAnchorType>::toEnum(value.get<std::string>());
+ return Enum<SymbolAnchorType>::toEnum(value.get<std::string>());
}
template <>
diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp
index 75dc83206c..e439e42695 100644
--- a/src/mbgl/style/image_impl.hpp
+++ b/src/mbgl/style/image_impl.hpp
@@ -28,5 +28,6 @@ public:
using ImageMap = std::unordered_map<std::string, Immutable<style::Image::Impl>>;
using ImageDependencies = std::set<std::string>;
+using ImageRequestPair = std::pair<ImageDependencies, uint64_t>;
} // namespace mbgl
diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp
index e37382d5ef..854c771847 100644
--- a/src/mbgl/style/layers/custom_layer.cpp
+++ b/src/mbgl/style/layers/custom_layer.cpp
@@ -8,9 +8,18 @@ namespace style {
CustomLayer::CustomLayer(const std::string& layerID,
CustomLayerInitializeFunction init,
CustomLayerRenderFunction render,
+ CustomLayerContextLostFunction contextLost,
CustomLayerDeinitializeFunction deinit,
void* context)
- : Layer(makeMutable<Impl>(layerID, init, render, deinit, context)) {
+ : Layer(makeMutable<Impl>(layerID, init, render, contextLost, deinit, context)) {
+}
+
+CustomLayer::CustomLayer(const std::string& layerID,
+ CustomLayerInitializeFunction init,
+ CustomLayerRenderFunction render,
+ CustomLayerDeinitializeFunction deinit,
+ void* context)
+ : Layer(makeMutable<Impl>(layerID, init, render, nullptr, deinit, context)) {
}
CustomLayer::~CustomLayer() = default;
diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp
index 42e60c582c..1de268d2e2 100644
--- a/src/mbgl/style/layers/custom_layer_impl.cpp
+++ b/src/mbgl/style/layers/custom_layer_impl.cpp
@@ -6,12 +6,14 @@ namespace style {
CustomLayer::Impl::Impl(const std::string& id_,
CustomLayerInitializeFunction initializeFn_,
CustomLayerRenderFunction renderFn_,
+ CustomLayerContextLostFunction contextLostFn_,
CustomLayerDeinitializeFunction deinitializeFn_,
void* context_)
: Layer::Impl(LayerType::Custom, id_, std::string()) {
initializeFn = initializeFn_;
renderFn = renderFn_;
deinitializeFn = deinitializeFn_;
+ contextLostFn = contextLostFn_;
context = context_;
}
diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp
index defbbe6894..62efbbe15b 100644
--- a/src/mbgl/style/layers/custom_layer_impl.hpp
+++ b/src/mbgl/style/layers/custom_layer_impl.hpp
@@ -14,6 +14,7 @@ public:
Impl(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
+ CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);
@@ -22,6 +23,7 @@ public:
CustomLayerInitializeFunction initializeFn = nullptr;
CustomLayerRenderFunction renderFn = nullptr;
+ CustomLayerContextLostFunction contextLostFn = nullptr;
CustomLayerDeinitializeFunction deinitializeFn = nullptr;
void* context = nullptr;
};
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 803ae7397e..9a944657ca 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -332,6 +332,22 @@ void SymbolLayer::setIconOffset(DataDrivenPropertyValue<std::array<float, 2>> va
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultIconAnchor() {
+ return IconAnchor::defaultValue();
+}
+
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getIconAnchor() const {
+ return impl().layout.get<IconAnchor>();
+}
+
+void SymbolLayer::setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) {
+ if (value == getIconAnchor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconAnchor>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
PropertyValue<AlignmentType> SymbolLayer::getDefaultIconPitchAlignment() {
return IconPitchAlignment::defaultValue();
}
@@ -428,15 +444,15 @@ void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
return TextMaxWidth::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextMaxWidth() const {
+DataDrivenPropertyValue<float> SymbolLayer::getTextMaxWidth() const {
return impl().layout.get<TextMaxWidth>();
}
-void SymbolLayer::setTextMaxWidth(PropertyValue<float> value) {
+void SymbolLayer::setTextMaxWidth(DataDrivenPropertyValue<float> value) {
if (value == getTextMaxWidth())
return;
auto impl_ = mutableImpl();
@@ -460,15 +476,15 @@ void SymbolLayer::setTextLineHeight(PropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
return TextLetterSpacing::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
+DataDrivenPropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
return impl().layout.get<TextLetterSpacing>();
}
-void SymbolLayer::setTextLetterSpacing(PropertyValue<float> value) {
+void SymbolLayer::setTextLetterSpacing(DataDrivenPropertyValue<float> value) {
if (value == getTextLetterSpacing())
return;
auto impl_ = mutableImpl();
@@ -492,15 +508,15 @@ void SymbolLayer::setTextJustify(DataDrivenPropertyValue<TextJustifyType> value)
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-DataDrivenPropertyValue<TextAnchorType> SymbolLayer::getDefaultTextAnchor() {
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultTextAnchor() {
return TextAnchor::defaultValue();
}
-DataDrivenPropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const {
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getTextAnchor() const {
return impl().layout.get<TextAnchor>();
}
-void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<TextAnchorType> value) {
+void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) {
if (value == getTextAnchor())
return;
auto impl_ = mutableImpl();
diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp
index fe6ab38e92..436b5cbd4f 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -87,6 +87,11 @@ struct IconOffset : DataDrivenLayoutProperty<std::array<float, 2>> {
static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; }
};
+struct IconAnchor : DataDrivenLayoutProperty<SymbolAnchorType> {
+ static constexpr const char * key = "icon-anchor";
+ static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; }
+};
+
struct IconPitchAlignment : LayoutProperty<AlignmentType> {
static constexpr const char * key = "icon-pitch-alignment";
static AlignmentType defaultValue() { return AlignmentType::Auto; }
@@ -117,7 +122,7 @@ struct TextSize : DataDrivenLayoutProperty<float> {
static float defaultValue() { return 16; }
};
-struct TextMaxWidth : LayoutProperty<float> {
+struct TextMaxWidth : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "text-max-width";
static float defaultValue() { return 10; }
};
@@ -127,7 +132,7 @@ struct TextLineHeight : LayoutProperty<float> {
static float defaultValue() { return 1.2; }
};
-struct TextLetterSpacing : LayoutProperty<float> {
+struct TextLetterSpacing : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "text-letter-spacing";
static float defaultValue() { return 0; }
};
@@ -137,9 +142,9 @@ struct TextJustify : DataDrivenLayoutProperty<TextJustifyType> {
static TextJustifyType defaultValue() { return TextJustifyType::Center; }
};
-struct TextAnchor : DataDrivenLayoutProperty<TextAnchorType> {
+struct TextAnchor : DataDrivenLayoutProperty<SymbolAnchorType> {
static constexpr const char * key = "text-anchor";
- static TextAnchorType defaultValue() { return TextAnchorType::Center; }
+ static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; }
};
struct TextMaxAngle : LayoutProperty<float> {
@@ -259,6 +264,7 @@ class SymbolLayoutProperties : public Properties<
IconPadding,
IconKeepUpright,
IconOffset,
+ IconAnchor,
IconPitchAlignment,
TextPitchAlignment,
TextRotationAlignment,
diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp
index ea19c599e9..cc6378b366 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -1,7 +1,6 @@
#pragma once
#include <mbgl/style/source_observer.hpp>
-#include <mbgl/map/update.hpp>
#include <exception>
@@ -12,7 +11,7 @@ class Observer : public SourceObserver {
public:
virtual void onStyleLoading() {}
virtual void onStyleLoaded() {}
- virtual void onUpdate(Update) {}
+ virtual void onUpdate() {}
virtual void onStyleError(std::exception_ptr) {}
virtual void onResourceError(std::exception_ptr) {}
};
diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp
index efa4018b46..fd6d7d3013 100644
--- a/src/mbgl/style/sources/geojson_source_impl.cpp
+++ b/src/mbgl/style/sources/geojson_source_impl.cpp
@@ -71,7 +71,7 @@ GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON)
GeoJSONSource::Impl::~Impl() = default;
Range<uint8_t> GeoJSONSource::Impl::getZoomRange() const {
- return { 0, options.maxzoom };
+ return { options.minzoom, options.maxzoom };
}
GeoJSONData* GeoJSONSource::Impl::getData() const {
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 5fe1ab4a06..bd8631fc52 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -34,20 +34,8 @@ std::string Style::getName() const {
return impl->getName();
}
-LatLng Style::getDefaultLatLng() const {
- return impl->getDefaultLatLng();
-}
-
-double Style::getDefaultZoom() const {
- return impl->getDefaultZoom();
-}
-
-double Style::getDefaultBearing() const {
- return impl->getDefaultBearing();
-}
-
-double Style::getDefaultPitch() const {
- return impl->getDefaultPitch();
+CameraOptions Style::getDefaultCamera() const {
+ return impl->getDefaultCamera();
}
TransitionOptions Style::getTransitionOptions() const {
diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp
index 604af4be20..37907d3f60 100644
--- a/src/mbgl/style/style_impl.cpp
+++ b/src/mbgl/style/style_impl.cpp
@@ -88,7 +88,7 @@ void Style::Impl::parse(const std::string& json_) {
}
mutated = false;
- loaded = true;
+ loaded = false;
json = json_;
sources.clear();
@@ -107,16 +107,18 @@ void Style::Impl::parse(const std::string& json_) {
}
name = parser.name;
- defaultLatLng = parser.latLng;
- defaultZoom = parser.zoom;
- defaultBearing = parser.bearing;
- defaultPitch = parser.pitch;
+ defaultCamera.center = parser.latLng;
+ defaultCamera.zoom = parser.zoom;
+ defaultCamera.angle = parser.bearing;
+ defaultCamera.pitch = parser.pitch;
+
setLight(std::make_unique<Light>(parser.light));
spriteLoaded = false;
spriteLoader->load(parser.spriteURL, scheduler, fileSource);
glyphURL = parser.glyphURL;
+ loaded = true;
observer->onStyleLoaded();
}
@@ -202,7 +204,7 @@ Layer* Style::Impl::addLayer(std::unique_ptr<Layer> layer, optional<std::string>
}
layer->setObserver(this);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
return layers.add(std::move(layer), before);
}
@@ -212,7 +214,7 @@ std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) {
if (layer) {
layer->setObserver(nullptr);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
return layer;
@@ -232,20 +234,8 @@ std::string Style::Impl::getName() const {
return name;
}
-LatLng Style::Impl::getDefaultLatLng() const {
- return defaultLatLng;
-}
-
-double Style::Impl::getDefaultZoom() const {
- return defaultZoom;
-}
-
-double Style::Impl::getDefaultBearing() const {
- return defaultBearing;
-}
-
-double Style::Impl::getDefaultPitch() const {
- return defaultPitch;
+CameraOptions Style::Impl::getDefaultCamera() const {
+ return defaultCamera;
}
std::vector<Source*> Style::Impl::getSources() {
@@ -299,13 +289,13 @@ void Style::Impl::setObserver(style::Observer* observer_) {
void Style::Impl::onSourceLoaded(Source& source) {
sources.update(source);
observer->onSourceLoaded(source);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onSourceChanged(Source& source) {
sources.update(source);
observer->onSourceChanged(source);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onSourceError(Source& source, std::exception_ptr error) {
@@ -329,7 +319,7 @@ void Style::Impl::onSpriteLoaded(std::vector<std::unique_ptr<Image>>&& images_)
addImage(std::move(image));
}
spriteLoaded = true;
- observer->onUpdate(Update::Repaint); // For *-pattern properties.
+ observer->onUpdate(); // For *-pattern properties.
}
void Style::Impl::onSpriteError(std::exception_ptr error) {
@@ -340,11 +330,11 @@ void Style::Impl::onSpriteError(std::exception_ptr error) {
void Style::Impl::onLayerChanged(Layer& layer) {
layers.update(layer);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onLightChanged(const Light&) {
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::dumpDebugLogs() const {
diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp
index 76f244d5a4..3dc222bfad 100644
--- a/src/mbgl/style/style_impl.hpp
+++ b/src/mbgl/style/style_impl.hpp
@@ -12,6 +12,8 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/collection.hpp>
+#include <mbgl/map/camera.hpp>
+
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/geo.hpp>
@@ -69,10 +71,7 @@ public:
std::unique_ptr<Layer> removeLayer(const std::string& layerID);
std::string getName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
+ CameraOptions getDefaultCamera() const;
TransitionOptions getTransitionOptions() const;
void setTransitionOptions(const TransitionOptions&);
@@ -117,10 +116,7 @@ private:
// Defaults
std::string name;
- LatLng defaultLatLng;
- double defaultZoom = 0;
- double defaultBearing = 0;
- double defaultPitch = 0;
+ CameraOptions defaultCamera;
// SpriteLoaderObserver implementation.
void onSpriteLoaded(std::vector<std::unique_ptr<Image>>&&) override;
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
index 4fbf767e11..0a1781e01b 100644
--- a/src/mbgl/style/types.cpp
+++ b/src/mbgl/style/types.cpp
@@ -53,16 +53,16 @@ MBGL_DEFINE_ENUM(SymbolPlacementType, {
{ SymbolPlacementType::Line, "line" },
});
-MBGL_DEFINE_ENUM(TextAnchorType, {
- { TextAnchorType::Center, "center" },
- { TextAnchorType::Left, "left" },
- { TextAnchorType::Right, "right" },
- { TextAnchorType::Top, "top" },
- { TextAnchorType::Bottom, "bottom" },
- { TextAnchorType::TopLeft, "top-left" },
- { TextAnchorType::TopRight, "top-right" },
- { TextAnchorType::BottomLeft, "bottom-left" },
- { TextAnchorType::BottomRight, "bottom-right" }
+MBGL_DEFINE_ENUM(SymbolAnchorType, {
+ { SymbolAnchorType::Center, "center" },
+ { SymbolAnchorType::Left, "left" },
+ { SymbolAnchorType::Right, "right" },
+ { SymbolAnchorType::Top, "top" },
+ { SymbolAnchorType::Bottom, "bottom" },
+ { SymbolAnchorType::TopLeft, "top-left" },
+ { SymbolAnchorType::TopRight, "top-right" },
+ { SymbolAnchorType::BottomLeft, "bottom-left" },
+ { SymbolAnchorType::BottomRight, "bottom-right" }
});
MBGL_DEFINE_ENUM(TextJustifyType, {
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 19ecdfd17c..6cccb72ebe 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -58,13 +58,13 @@ using GlyphMap = std::map<FontStack, Glyphs>;
class PositionedGlyph {
public:
- explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, float angle_)
- : glyph(glyph_), x(x_), y(y_), angle(angle_) {}
+ explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_)
+ : glyph(glyph_), x(x_), y(y_), vertical(vertical_) {}
GlyphID glyph = 0;
float x = 0;
float y = 0;
- float angle = 0;
+ bool vertical = false;
};
enum class WritingModeType : uint8_t;
diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp
index 916d39ae62..c79a1938c1 100644
--- a/src/mbgl/text/glyph_manager.cpp
+++ b/src/mbgl/text/glyph_manager.cpp
@@ -36,8 +36,9 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
for (const auto& range : ranges) {
auto it = entry.ranges.find(range);
if (it == entry.ranges.end() || !it->second.parsed) {
- GlyphRequest& request = requestRange(entry, fontStack, range);
+ GlyphRequest& request = entry.ranges[range];
request.requestors[&requestor] = dependencies;
+ requestRange(request, fontStack, range);
}
}
}
@@ -49,18 +50,14 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
}
}
-GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
- GlyphRequest& request = entry.ranges[range];
-
+void GlyphManager::requestRange(GlyphRequest& request, const FontStack& fontStack, const GlyphRange& range) {
if (request.req) {
- return request;
+ return;
}
request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) {
processResponse(res, fontStack, range);
});
-
- return request;
}
void GlyphManager::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) {
diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp
index 00df079462..de2b9cde7b 100644
--- a/src/mbgl/text/glyph_manager.hpp
+++ b/src/mbgl/text/glyph_manager.hpp
@@ -58,7 +58,7 @@ private:
std::unordered_map<FontStack, Entry, FontStackHash> entries;
- GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&);
+ void requestRange(GlyphRequest&, const FontStack&, const GlyphRange&);
void processResponse(const Response&, const FontStack&, const GlyphRange&);
void notify(GlyphRequestor&, const GlyphDependencies&);
diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp
index 1e1279341d..48b24b5f41 100644
--- a/src/mbgl/text/placement_config.hpp
+++ b/src/mbgl/text/placement_config.hpp
@@ -13,9 +13,9 @@ public:
bool operator==(const PlacementConfig& rhs) const {
return angle == rhs.angle &&
pitch == rhs.pitch &&
- cameraToCenterDistance == rhs.cameraToCenterDistance &&
- (pitch * util::RAD2DEG < 25 || cameraToTileDistance == rhs.cameraToTileDistance) &&
- debug == rhs.debug;
+ debug == rhs.debug &&
+ ((pitch * util::RAD2DEG < 25) ||
+ (cameraToCenterDistance == rhs.cameraToCenterDistance && cameraToTileDistance == rhs.cameraToTileDistance));
}
bool operator!=(const PlacementConfig& rhs) const {
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index 7908ea4abc..0014ae8d01 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -133,18 +133,27 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
const float x2 = x1 + rect.w;
const float y2 = y1 + rect.h;
- const Point<float> center{builtInOffset.x - halfAdvance, static_cast<float>(static_cast<float>(glyph.metrics.advance) / 2.0)};
-
Point<float> tl{x1, y1};
Point<float> tr{x2, y1};
Point<float> bl{x1, y2};
Point<float> br{x2, y2};
- if (positionedGlyph.angle != 0) {
- tl = util::rotate(tl - center, positionedGlyph.angle) + center;
- tr = util::rotate(tr - center, positionedGlyph.angle) + center;
- bl = util::rotate(bl - center, positionedGlyph.angle) + center;
- br = util::rotate(br - center, positionedGlyph.angle) + center;
+ if (alongLine && positionedGlyph.vertical) {
+ // Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em)
+ // In horizontal orientation, the y values for glyphs are below the midline
+ // and we use a "yOffset" of -17 to pull them up to the middle.
+ // By rotating counter-clockwise around the point at the center of the left
+ // edge of a 24x24 layout box centered below the midline, we align the center
+ // of the glyphs with the horizontal midline, so the yOffset is no longer
+ // necessary, but we also pull the glyph to the left along the x axis
+ const Point<float> center{-halfAdvance, halfAdvance};
+ const float verticalRotation = -M_PI_2;
+ const Point<float> xOffsetCorrection{5, 0};
+
+ tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection;
+ tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection;
+ bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection;
+ br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection;
}
if (textRotate) {
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 4a206e9bae..5d688ea539 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -11,12 +11,63 @@
namespace mbgl {
-PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
+struct AnchorAlignment {
+ AnchorAlignment(float horizontal_, float vertical_)
+ : horizontalAlign(horizontal_), verticalAlign(vertical_) {
+ }
+
+ float horizontalAlign;
+ float verticalAlign;
+};
+
+AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) {
+ float horizontalAlign = 0.5;
+ float verticalAlign = 0.5;
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::TopRight:
+ case style::SymbolAnchorType::BottomRight:
+ horizontalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::BottomLeft:
+ horizontalAlign = 0;
+ break;
+ }
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::BottomLeft:
+ case style::SymbolAnchorType::BottomRight:
+ verticalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::TopRight:
+ verticalAlign = 0;
+ break;
+ }
+
+ return AnchorAlignment(horizontalAlign, verticalAlign);
+}
+
+PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) {
+ AnchorAlignment anchorAlign = getAnchorAlignment(iconAnchor);
float dx = iconOffset[0];
float dy = iconOffset[1];
- float x1 = dx - image.displaySize()[0] / 2.0f;
+ float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
float x2 = x1 + image.displaySize()[0];
- float y1 = dy - image.displaySize()[1] / 2.0f;
+ float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
float y2 = y1 + image.displaySize()[1];
return PositionedIcon { image, y1, y2, x1, x2, iconRotation };
@@ -200,7 +251,7 @@ void shapeLines(Shaping& shaping,
const std::vector<std::u16string>& lines,
const float spacing,
const float lineHeight,
- const style::TextAnchorType textAnchor,
+ const style::SymbolAnchorType textAnchor,
const style::TextJustifyType textJustify,
const float verticalHeight,
const WritingModeType writingMode,
@@ -237,10 +288,10 @@ void shapeLines(Shaping& shaping,
const Glyph& glyph = **it->second;
if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) {
- shaping.positionedGlyphs.emplace_back(chr, x, y, 0);
+ shaping.positionedGlyphs.emplace_back(chr, x, y, false);
x += glyph.metrics.advance + spacing;
} else {
- shaping.positionedGlyphs.emplace_back(chr, x, 0, -M_PI_2);
+ shaping.positionedGlyphs.emplace_back(chr, x, 0, true);
x += verticalHeight + spacing;
}
}
@@ -258,58 +309,23 @@ void shapeLines(Shaping& shaping,
y += lineHeight;
}
- float horizontalAlign = 0.5;
- float verticalAlign = 0.5;
-
- switch (textAnchor) {
- case style::TextAnchorType::Top:
- case style::TextAnchorType::Bottom:
- case style::TextAnchorType::Center:
- break;
- case style::TextAnchorType::Right:
- case style::TextAnchorType::TopRight:
- case style::TextAnchorType::BottomRight:
- horizontalAlign = 1;
- break;
- case style::TextAnchorType::Left:
- case style::TextAnchorType::TopLeft:
- case style::TextAnchorType::BottomLeft:
- horizontalAlign = 0;
- break;
- }
-
- switch (textAnchor) {
- case style::TextAnchorType::Left:
- case style::TextAnchorType::Right:
- case style::TextAnchorType::Center:
- break;
- case style::TextAnchorType::Bottom:
- case style::TextAnchorType::BottomLeft:
- case style::TextAnchorType::BottomRight:
- verticalAlign = 1;
- break;
- case style::TextAnchorType::Top:
- case style::TextAnchorType::TopLeft:
- case style::TextAnchorType::TopRight:
- verticalAlign = 0;
- break;
- }
+ auto anchorAlign = getAnchorAlignment(textAnchor);
- align(shaping, justify, horizontalAlign, verticalAlign,
- maxLineLength, lineHeight, lines.size());
+ align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
+ lineHeight, lines.size());
const uint32_t height = lines.size() * lineHeight;
// Calculate the bounding box
- shaping.top += -verticalAlign * height;
+ shaping.top += -anchorAlign.verticalAlign * height;
shaping.bottom = shaping.top + height;
- shaping.left += -horizontalAlign * maxLineLength;
+ shaping.left += -anchorAlign.horizontalAlign * maxLineLength;
shaping.right = shaping.left + maxLineLength;
}
const Shaping getShaping(const std::u16string& logicalInput,
const float maxWidth,
const float lineHeight,
- const style::TextAnchorType textAnchor,
+ const style::SymbolAnchorType textAnchor,
const style::TextJustifyType textJustify,
const float spacing,
const Point<float>& translate,
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index 00e4ec55f8..0a961849e5 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -32,7 +32,10 @@ private:
float _angle;
public:
- static PositionedIcon shapeIcon(const ImagePosition&, const std::array<float, 2>& iconOffset, const float iconRotation);
+ static PositionedIcon shapeIcon(const ImagePosition&,
+ const std::array<float, 2>& iconOffset,
+ style::SymbolAnchorType iconAnchor,
+ const float iconRotation);
const ImagePosition& image() const { return _image; }
float top() const { return _top; }
@@ -45,7 +48,7 @@ public:
const Shaping getShaping(const std::u16string& string,
float maxWidth,
float lineHeight,
- style::TextAnchorType textAnchor,
+ style::SymbolAnchorType textAnchor,
style::TextJustifyType textJustify,
float spacing,
const Point<float>& translate,
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index f6861140b7..ee4989462c 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -1,103 +1,11 @@
#include <mbgl/tile/geojson_tile.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/tile/geojson_tile_data.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/util/string.hpp>
-
-#include <mapbox/geojsonvt.hpp>
-#include <supercluster.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
namespace mbgl {
-// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have
-// one layer, and it is always returned regardless of which layer is requested.
-
-class GeoJSONTileFeature : public GeometryTileFeature {
-public:
- const mapbox::geometry::feature<int16_t>& feature;
-
- GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& feature_)
- : feature(feature_) {
- }
-
- FeatureType getType() const override {
- return apply_visitor(ToFeatureType(), feature.geometry);
- }
-
- PropertyMap getProperties() const override {
- return feature.properties;
- }
-
- optional<FeatureIdentifier> getID() const override {
- return feature.id;
- }
-
- GeometryCollection getGeometries() const override {
- GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry);
-
- // https://github.com/mapbox/geojson-vt-cpp/issues/44
- if (getType() == FeatureType::Polygon) {
- geometry = fixupPolygons(geometry);
- }
-
- return geometry;
- }
-
- optional<Value> getValue(const std::string& key) const override {
- auto it = feature.properties.find(key);
- if (it != feature.properties.end()) {
- return optional<Value>(it->second);
- }
- return optional<Value>();
- }
-};
-
-class GeoJSONTileLayer : public GeometryTileLayer {
-public:
- GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
- : features(std::move(features_)) {
- }
-
- std::size_t featureCount() const override {
- return features->size();
- }
-
- std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
- return std::make_unique<GeoJSONTileFeature>((*features)[i]);
- }
-
- std::string getName() const override {
- return "";
- }
-
-private:
- std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
-};
-
-class GeoJSONTileData : public GeometryTileData {
-public:
- GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_)
- : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>(
- std::move(features_))) {
- }
-
- GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
- : features(std::move(features_)) {
- }
-
- std::unique_ptr<GeometryTileData> clone() const override {
- return std::make_unique<GeoJSONTileData>(features);
- }
-
- std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override {
- return std::make_unique<GeoJSONTileLayer>(features);
- }
-
-
-private:
- std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
-};
-
GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
std::string sourceID_,
const TileParameters& parameters,
diff --git a/src/mbgl/tile/geojson_tile_data.hpp b/src/mbgl/tile/geojson_tile_data.hpp
new file mode 100644
index 0000000000..3402c2a009
--- /dev/null
+++ b/src/mbgl/tile/geojson_tile_data.hpp
@@ -0,0 +1,94 @@
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+
+// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have
+// one layer, and it is always returned regardless of which layer is requested.
+
+class GeoJSONTileFeature : public GeometryTileFeature {
+public:
+ const mapbox::geometry::feature<int16_t>& feature;
+
+ GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& feature_)
+ : feature(feature_) {
+ }
+
+ FeatureType getType() const override {
+ return apply_visitor(ToFeatureType(), feature.geometry);
+ }
+
+ PropertyMap getProperties() const override {
+ return feature.properties;
+ }
+
+ optional<FeatureIdentifier> getID() const override {
+ return feature.id;
+ }
+
+ GeometryCollection getGeometries() const override {
+ GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry);
+
+ // https://github.com/mapbox/geojson-vt-cpp/issues/44
+ if (getType() == FeatureType::Polygon) {
+ geometry = fixupPolygons(geometry);
+ }
+
+ return geometry;
+ }
+
+ optional<Value> getValue(const std::string& key) const override {
+ auto it = feature.properties.find(key);
+ if (it != feature.properties.end()) {
+ return optional<Value>(it->second);
+ }
+ return optional<Value>();
+ }
+};
+
+class GeoJSONTileLayer : public GeometryTileLayer {
+public:
+ GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
+ : features(std::move(features_)) {
+ }
+
+ std::size_t featureCount() const override {
+ return features->size();
+ }
+
+ std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
+ return std::make_unique<GeoJSONTileFeature>((*features)[i]);
+ }
+
+ std::string getName() const override {
+ return "";
+ }
+
+private:
+ std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
+};
+
+class GeoJSONTileData : public GeometryTileData {
+public:
+ GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_)
+ : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>(
+ std::move(features_))) {
+ }
+
+ GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
+ : features(std::move(features_)) {
+ }
+
+ std::unique_ptr<GeometryTileData> clone() const override {
+ return std::make_unique<GeoJSONTileData>(features);
+ }
+
+ std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override {
+ return std::make_unique<GeoJSONTileLayer>(features);
+ }
+
+
+private:
+ std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 33911df9ed..8c018ce3aa 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -17,10 +17,9 @@
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/map/transform_state.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/util/chrono.hpp>
#include <mbgl/util/logging.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <iostream>
@@ -28,12 +27,28 @@ namespace mbgl {
using namespace style;
+/*
+ Correlation between GeometryTile and GeometryTileWorker is safeguarded by two
+ correlation schemes:
+
+ GeometryTile's 'correlationID' is used for ensuring the tile will be flagged
+ as non-pending only when the placement coming from the last operation (as in
+ 'setData', 'setLayers', 'setPlacementConfig') occurs. This is important for
+ still mode rendering as we want to render only when all layout and placement
+ operations are completed.
+
+ GeometryTileWorker's 'imageCorrelationID' is used for checking whether an
+ image request reply coming from `GeometryTile` is valid. Previous image
+ request replies are ignored as they result in incomplete placement attempts
+ that could flag the tile as non-pending too early.
+ */
+
GeometryTile::GeometryTile(const OverscaledTileID& id_,
std::string sourceID_,
const TileParameters& parameters)
: Tile(id_),
sourceID(std::move(sourceID_)),
- mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(parameters.workerScheduler,
ActorRef<GeometryTile>(*this, mailbox),
id_,
@@ -42,8 +57,8 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_,
parameters.pixelRatio),
glyphManager(parameters.glyphManager),
imageManager(parameters.imageManager),
- placementThrottler(Milliseconds(300), [this] { invokePlacement(); }),
- lastYStretch(1.0f) {
+ lastYStretch(1.0f),
+ mode(parameters.mode) {
}
GeometryTile::~GeometryTile() {
@@ -62,7 +77,6 @@ void GeometryTile::markObsolete() {
void GeometryTile::setError(std::exception_ptr err) {
loaded = true;
- renderable = false;
observer->onTileError(*this, err);
}
@@ -86,7 +100,7 @@ void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
++correlationID;
requestedConfig = desiredConfig;
- placementThrottler.invoke();
+ invokePlacement();
}
void GeometryTile::invokePlacement() {
@@ -120,9 +134,10 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers)
worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID);
}
-void GeometryTile::onLayout(LayoutResult result) {
+void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
loaded = true;
renderable = true;
+ (void)resultCorrelationID;
nonSymbolBuckets = std::move(result.nonSymbolBuckets);
featureIndex = std::move(result.featureIndex);
data = std::move(result.tileData);
@@ -130,10 +145,10 @@ void GeometryTile::onLayout(LayoutResult result) {
observer->onTileChanged(*this);
}
-void GeometryTile::onPlacement(PlacementResult result) {
+void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) {
loaded = true;
renderable = true;
- if (result.correlationID == correlationID) {
+ if (resultCorrelationID == correlationID) {
pending = false;
}
symbolBuckets = std::move(result.symbolBuckets);
@@ -150,10 +165,11 @@ void GeometryTile::onPlacement(PlacementResult result) {
observer->onTileChanged(*this);
}
-void GeometryTile::onError(std::exception_ptr err) {
+void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {
loaded = true;
- pending = false;
- renderable = false;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
observer->onTileError(*this, err);
}
@@ -165,12 +181,12 @@ void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) {
glyphManager.getGlyphs(*this, std::move(glyphDependencies));
}
-void GeometryTile::onImagesAvailable(ImageMap images) {
- worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images));
+void GeometryTile::onImagesAvailable(ImageMap images, uint64_t imageCorrelationID) {
+ worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), imageCorrelationID);
}
-void GeometryTile::getImages(ImageDependencies imageDependencies) {
- imageManager.getImages(*this, std::move(imageDependencies));
+void GeometryTile::getImages(ImageRequestPair pair) {
+ imageManager.getImages(*this, std::move(pair));
}
void GeometryTile::upload(gl::Context& context) {
@@ -214,11 +230,20 @@ void GeometryTile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState& transformState,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) {
if (!featureIndex || !data) return;
+ // Determine the additional radius needed factoring in property functions
+ float additionalRadius = 0;
+ for (const RenderLayer* layer : layers) {
+ auto bucket = getBucket(*layer->baseImpl);
+ if (bucket) {
+ additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer));
+ }
+ }
+
featureIndex->query(result,
queryGeometry,
transformState.getAngle(),
@@ -227,9 +252,9 @@ void GeometryTile::queryRenderedFeatures(
options,
*data,
id.canonical,
- style,
+ layers,
collisionTile.get(),
- *this);
+ additionalRadius);
}
void GeometryTile::querySourceFeatures(
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index c45762742b..a478aad504 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -19,7 +19,6 @@
namespace mbgl {
class GeometryTileData;
-class RenderStyle;
class RenderLayer;
class SourceQueryOptions;
class TileParameters;
@@ -41,10 +40,10 @@ public:
void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override;
void onGlyphsAvailable(GlyphMap) override;
- void onImagesAvailable(ImageMap) override;
+ void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override;
void getGlyphs(GlyphDependencies);
- void getImages(ImageDependencies);
+ void getImages(ImageRequestPair);
void upload(gl::Context&) override;
Bucket* getBucket(const style::Layer::Impl&) const override;
@@ -56,7 +55,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>& layers,
const RenderedQueryOptions& options) override;
void querySourceFeatures(
@@ -70,18 +69,15 @@ public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<GeometryTileData> tileData;
- uint64_t correlationID;
LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_,
std::unique_ptr<FeatureIndex> featureIndex_,
- std::unique_ptr<GeometryTileData> tileData_,
- uint64_t correlationID_)
+ std::unique_ptr<GeometryTileData> tileData_)
: nonSymbolBuckets(std::move(nonSymbolBuckets_)),
featureIndex(std::move(featureIndex_)),
- tileData(std::move(tileData_)),
- correlationID(correlationID_) {}
+ tileData(std::move(tileData_)) {}
};
- void onLayout(LayoutResult);
+ void onLayout(LayoutResult, uint64_t correlationID);
class PlacementResult {
public:
@@ -89,22 +85,19 @@ public:
std::unique_ptr<CollisionTile> collisionTile;
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
- uint64_t correlationID;
PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
std::unique_ptr<CollisionTile> collisionTile_,
optional<AlphaImage> glyphAtlasImage_,
- optional<PremultipliedImage> iconAtlasImage_,
- uint64_t correlationID_)
+ optional<PremultipliedImage> iconAtlasImage_)
: symbolBuckets(std::move(symbolBuckets_)),
collisionTile(std::move(collisionTile_)),
glyphAtlasImage(std::move(glyphAtlasImage_)),
- iconAtlasImage(std::move(iconAtlasImage_)),
- correlationID(correlationID_) {}
+ iconAtlasImage(std::move(iconAtlasImage_)) {}
};
- void onPlacement(PlacementResult);
+ void onPlacement(PlacementResult, uint64_t correlationID);
- void onError(std::exception_ptr);
+ void onError(std::exception_ptr, uint64_t correlationID);
float yStretch() const override;
@@ -140,9 +133,9 @@ private:
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
std::unique_ptr<CollisionTile> collisionTile;
-
- util::Throttler placementThrottler;
+
float lastYStretch;
+ const MapMode mode;
public:
optional<gl::Texture> glyphAtlasTexture;
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index c622d82e31..50429420c3 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -88,7 +88,7 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_,
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -112,7 +112,7 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_,
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -136,7 +136,7 @@ void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, ui
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -161,7 +161,7 @@ void GeometryTileWorker::symbolDependenciesChanged() {
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -187,7 +187,7 @@ void GeometryTileWorker::coalesced() {
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -216,7 +216,10 @@ void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) {
symbolDependenciesChanged();
}
-void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap) {
+void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap, uint64_t imageCorrelationID_) {
+ if (imageCorrelationID != imageCorrelationID_) {
+ return; // Ignore outdated image request replies.
+ }
imageMap = std::move(newImageMap);
pendingImageDependencies.clear();
symbolDependenciesChanged();
@@ -239,7 +242,7 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen
void GeometryTileWorker::requestNewImages(const ImageDependencies& imageDependencies) {
pendingImageDependencies = imageDependencies;
if (!pendingImageDependencies.empty()) {
- parent.invoke(&GeometryTile::getImages, pendingImageDependencies);
+ parent.invoke(&GeometryTile::getImages, std::make_pair(pendingImageDependencies, ++imageCorrelationID));
}
}
@@ -354,8 +357,7 @@ void GeometryTileWorker::redoLayout() {
std::move(buckets),
std::move(featureIndex),
*data ? (*data)->clone() : nullptr,
- correlationID
- });
+ }, correlationID);
attemptPlacement();
}
@@ -419,8 +421,7 @@ void GeometryTileWorker::attemptPlacement() {
std::move(collisionTile),
std::move(glyphAtlasImage),
std::move(iconAtlasImage),
- correlationID
- });
+ }, correlationID);
}
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 7f80c3b4f7..1425daa7a1 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -38,7 +38,7 @@ public:
void setPlacementConfig(PlacementConfig, uint64_t correlationID);
void onGlyphsAvailable(GlyphMap glyphs);
- void onImagesAvailable(ImageMap images);
+ void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID);
private:
void coalesced();
@@ -70,6 +70,7 @@ private:
State state = Idle;
uint64_t correlationID = 0;
+ uint64_t imageCorrelationID = 0;
// Outer optional indicates whether we've received it or not.
optional<std::vector<Immutable<style::Layer::Impl>>> layers;
diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp
index 1260fd1edd..2a3c9eeb0e 100644
--- a/src/mbgl/tile/raster_tile.cpp
+++ b/src/mbgl/tile/raster_tile.cpp
@@ -8,7 +8,7 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/buckets/raster_bucket.hpp>
-#include <mbgl/util/run_loop.hpp>
+#include <mbgl/actor/scheduler.hpp>
namespace mbgl {
@@ -17,7 +17,7 @@ RasterTile::RasterTile(const OverscaledTileID& id_,
const Tileset& tileset)
: Tile(id_),
loader(*this, id_, parameters, tileset),
- mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(parameters.workerScheduler,
ActorRef<RasterTile>(*this, mailbox)) {
}
@@ -29,7 +29,6 @@ void RasterTile::cancel() {
void RasterTile::setError(std::exception_ptr err) {
loaded = true;
- renderable = false;
observer->onTileError(*this, err);
}
@@ -38,20 +37,27 @@ void RasterTile::setData(std::shared_ptr<const std::string> data,
optional<Timestamp> expires_) {
modified = modified_;
expires = expires_;
- worker.invoke(&RasterTileWorker::parse, data);
+
+ pending = true;
+ ++correlationID;
+ worker.invoke(&RasterTileWorker::parse, data, correlationID);
}
-void RasterTile::onParsed(std::unique_ptr<RasterBucket> result) {
+void RasterTile::onParsed(std::unique_ptr<RasterBucket> result, const uint64_t resultCorrelationID) {
bucket = std::move(result);
loaded = true;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
renderable = bucket ? true : false;
observer->onTileChanged(*this);
}
-void RasterTile::onError(std::exception_ptr err) {
- bucket.reset();
+void RasterTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {
loaded = true;
- renderable = false;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
observer->onTileError(*this, err);
}
diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp
index 28a27b2b37..2cb64e8ed7 100644
--- a/src/mbgl/tile/raster_tile.hpp
+++ b/src/mbgl/tile/raster_tile.hpp
@@ -36,8 +36,8 @@ public:
void setMask(TileMask&&) override;
- void onParsed(std::unique_ptr<RasterBucket> result);
- void onError(std::exception_ptr);
+ void onParsed(std::unique_ptr<RasterBucket> result, uint64_t correlationID);
+ void onError(std::exception_ptr, uint64_t correlationID);
private:
TileLoader<RasterTile> loader;
@@ -45,6 +45,8 @@ private:
std::shared_ptr<Mailbox> mailbox;
Actor<RasterTileWorker> worker;
+ uint64_t correlationID = 0;
+
// Contains the Bucket object for the tile. Buckets are render
// objects and they get added by tile parsing operations.
std::unique_ptr<RasterBucket> bucket;
diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp
index 3c8af97b40..4afa876429 100644
--- a/src/mbgl/tile/raster_tile_worker.cpp
+++ b/src/mbgl/tile/raster_tile_worker.cpp
@@ -10,17 +10,17 @@ RasterTileWorker::RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTi
: parent(std::move(parent_)) {
}
-void RasterTileWorker::parse(std::shared_ptr<const std::string> data) {
+void RasterTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID) {
if (!data) {
- parent.invoke(&RasterTile::onParsed, nullptr); // No data; empty tile.
+ parent.invoke(&RasterTile::onParsed, nullptr, correlationID); // No data; empty tile.
return;
}
try {
auto bucket = std::make_unique<RasterBucket>(decodeImage(*data));
- parent.invoke(&RasterTile::onParsed, std::move(bucket));
+ parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID);
} catch (...) {
- parent.invoke(&RasterTile::onError, std::current_exception());
+ parent.invoke(&RasterTile::onError, std::current_exception(), correlationID);
}
}
diff --git a/src/mbgl/tile/raster_tile_worker.hpp b/src/mbgl/tile/raster_tile_worker.hpp
index 44bc37ca5d..520973c3c3 100644
--- a/src/mbgl/tile/raster_tile_worker.hpp
+++ b/src/mbgl/tile/raster_tile_worker.hpp
@@ -13,7 +13,7 @@ class RasterTileWorker {
public:
RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTile>);
- void parse(std::shared_ptr<const std::string> data);
+ void parse(std::shared_ptr<const std::string> data, uint64_t correlationID);
private:
ActorRef<RasterTile> parent;
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index 35fc31dae1..7d7eb0b3fc 100644
--- a/src/mbgl/tile/tile.cpp
+++ b/src/mbgl/tile/tile.cpp
@@ -33,7 +33,7 @@ void Tile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>&,
const GeometryCoordinates&,
const TransformState&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const RenderedQueryOptions&) {}
void Tile::querySourceFeatures(
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index a1ab6a84b7..39cc0de8bd 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -23,7 +23,7 @@ class DebugBucket;
class TransformState;
class TileObserver;
class PlacementConfig;
-class RenderStyle;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
@@ -61,7 +61,7 @@ public:
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const RenderedQueryOptions& options);
virtual void querySourceFeatures(
diff --git a/src/mbgl/tile/tile_id_hash.cpp b/src/mbgl/tile/tile_id_hash.cpp
new file mode 100644
index 0000000000..4a1f185817
--- /dev/null
+++ b/src/mbgl/tile/tile_id_hash.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/tile/tile_id.hpp>
+
+#include <boost/functional/hash.hpp>
+
+namespace std {
+
+size_t hash<mbgl::CanonicalTileID>::operator()(const mbgl::CanonicalTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, id.x);
+ boost::hash_combine(seed, id.y);
+ boost::hash_combine(seed, id.z);
+ return seed;
+}
+
+size_t hash<mbgl::UnwrappedTileID>::operator()(const mbgl::UnwrappedTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
+ boost::hash_combine(seed, id.wrap);
+ return seed;
+}
+
+size_t hash<mbgl::OverscaledTileID>::operator()(const mbgl::OverscaledTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
+ boost::hash_combine(seed, id.overscaledZ);
+ return seed;
+}
+
+} // namespace std
diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp
index 899cbaf9b0..598ec32c10 100644
--- a/src/mbgl/tile/tile_loader_impl.hpp
+++ b/src/mbgl/tile/tile_loader_impl.hpp
@@ -61,7 +61,10 @@ void TileLoader<T>::loadOptional() {
// When the optional request could not be satisfied, don't treat it as an error.
// Instead, we make sure that the next request knows that there has been an optional
// request before by setting one of the prior* fields.
+ resource.priorModified = res.modified;
resource.priorExpires = Timestamp{ Seconds::zero() };
+ resource.priorEtag = res.etag;
+ resource.priorData = res.data;
} else {
loadedData(res);
}
diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp
index 9faef140ef..56f78c9885 100644
--- a/src/mbgl/util/constants.cpp
+++ b/src/mbgl/util/constants.cpp
@@ -11,7 +11,6 @@ const bool tileParseWarnings = false;
const bool styleParseWarnings = false;
const bool spriteWarnings = false;
const bool renderWarnings = false;
-const bool renderTree = false;
const bool labelTextMissingWarning = true;
const bool missingFontStackWarning = true;
const bool missingFontFaceWarning = true;
@@ -22,7 +21,6 @@ const bool tileParseWarnings = false;
const bool styleParseWarnings = false;
const bool spriteWarnings = false;
const bool renderWarnings = false;
-const bool renderTree = false;
const bool labelTextMissingWarning = false;
const bool missingFontStackWarning = false;
const bool missingFontFaceWarning = false;
diff --git a/src/mbgl/util/http_timeout.cpp b/src/mbgl/util/http_timeout.cpp
index ca9a93498f..3456369250 100644
--- a/src/mbgl/util/http_timeout.cpp
+++ b/src/mbgl/util/http_timeout.cpp
@@ -1,6 +1,8 @@
#include <mbgl/util/http_timeout.hpp>
#include <mbgl/util/constants.hpp>
+#include <cassert>
+
namespace mbgl {
namespace http {
diff --git a/src/mbgl/util/math.hpp b/src/mbgl/util/math.hpp
index eb3c7d0fde..c18ce0c254 100644
--- a/src/mbgl/util/math.hpp
+++ b/src/mbgl/util/math.hpp
@@ -77,9 +77,9 @@ T mag(const S& a) {
return std::sqrt(a.x * a.x + a.y * a.y);
}
-template <typename S>
+template <typename T = double, typename S>
S unit(const S& a) {
- auto magnitude = mag(a);
+ auto magnitude = mag<T>(a);
if (magnitude == 0) {
return a;
}
@@ -112,7 +112,7 @@ inline T division(const T dividend, const T divisor, const T nan) {
if (dividend == 0) {
return nan;
} else {
- return std::copysign(std::numeric_limits<T>::infinity(), dividend);
+ return ::copysign(std::numeric_limits<T>::infinity(), dividend);
}
} else {
return dividend / divisor;
diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp
index 77a1416e48..339e74b250 100644
--- a/src/mbgl/util/offscreen_texture.cpp
+++ b/src/mbgl/util/offscreen_texture.cpp
@@ -11,20 +11,22 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default;
class OffscreenTexture::Impl {
public:
- Impl(gl::Context& context_, const Size size_, OffscreenTextureAttachment type_)
- : context(context_), size(std::move(size_)), type(type_) {
+ Impl(gl::Context& context_, const Size size_)
+ : context(context_), size(std::move(size_)) {
+ assert(!size.isEmpty());
+ }
+ Impl(gl::Context& context_,
+ const Size size_,
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_)
+ : context(context_), size(std::move(size_)), depth(&depth_) {
assert(!size.isEmpty());
}
void bind() {
if (!framebuffer) {
texture = context.createTexture(size, gl::TextureFormat::RGBA);
-
- if (type == OffscreenTextureAttachment::Depth) {
- gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depth =
- context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size);
- framebuffer = context.createFramebuffer(*texture, depth);
-
+ if (depth) {
+ framebuffer = context.createFramebuffer(*texture, *depth);
} else {
framebuffer = context.createFramebuffer(*texture);
}
@@ -32,7 +34,7 @@ public:
context.bindFramebuffer = framebuffer->framebuffer;
}
- context.activeTexture = 0;
+ context.activeTextureUnit = 0;
context.scissorTest = false;
context.viewport = { 0, 0, size };
}
@@ -53,15 +55,21 @@ public:
private:
gl::Context& context;
const Size size;
- OffscreenTextureAttachment type;
optional<gl::Framebuffer> framebuffer;
optional<gl::Texture> texture;
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr;
};
OffscreenTexture::OffscreenTexture(gl::Context& context,
+ const Size size)
+ : impl(std::make_unique<Impl>(context, std::move(size))) {
+ assert(!size.isEmpty());
+}
+
+OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size,
- OffscreenTextureAttachment type)
- : impl(std::make_unique<Impl>(context, std::move(size), type)) {
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer)
+ : impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) {
assert(!size.isEmpty());
}
diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp
index 0353f3f9c5..7f7e0f0338 100644
--- a/src/mbgl/util/offscreen_texture.hpp
+++ b/src/mbgl/util/offscreen_texture.hpp
@@ -9,16 +9,13 @@ class Context;
class Texture;
} // namespace gl
-enum class OffscreenTextureAttachment {
- None,
- Depth,
-};
-
class OffscreenTexture {
public:
OffscreenTexture(gl::Context&,
- Size size = { 256, 256 },
- OffscreenTextureAttachment type = OffscreenTextureAttachment::None);
+ Size size = { 256, 256 });
+ OffscreenTexture(gl::Context&,
+ Size size,
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&);
~OffscreenTexture();
OffscreenTexture(OffscreenTexture&&);
OffscreenTexture& operator=(OffscreenTexture&&);
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index b53e91162c..a5a1b1d70c 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -169,5 +169,26 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
z);
}
+// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs
+// Computes the projected tiles for the lower left and upper right points of the bounds
+// and uses that to compute the tile cover count
+uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){
+
+ auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_);
+ auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_);
+
+ auto x1 = floor(sw.x/ tileSize_);
+ auto x2 = floor((ne.x - 1) / tileSize_);
+ auto y1 = floor(sw.y/ tileSize_);
+ auto y2 = floor((ne.y - 1) / tileSize_);
+
+ auto minX = std::fmax(std::min(x1, x2), 0);
+ auto maxX = std::max(x1, x2);
+ auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2);
+ auto maxY = (std::pow(2, zoom) - 1) - std::fmax(std::min(y1, y2), 0);
+
+ return (maxX - minX + 1) * (maxY - minY + 1);
+}
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index 2d32d8bf41..3c7a4ee44a 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -18,5 +18,8 @@ int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize);
std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z);
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z);
+// Compute only the count of tiles needed for tileCover
+uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize);
+
} // namespace util
} // namespace mbgl
diff --git a/test/actor/actor.test.cpp b/test/actor/actor.test.cpp
index 39d7ff81f4..967dc152d9 100644
--- a/test/actor/actor.test.cpp
+++ b/test/actor/actor.test.cpp
@@ -306,33 +306,55 @@ TEST(Actor, Ask) {
ASSERT_EQ(2, result.get());
}
+TEST(Actor, AskVoid) {
+ // Ask waits for void methods
+
+ struct Test {
+ bool& executed;
+
+ Test(bool& executed_) : executed(executed_) {
+ }
+
+ void doIt() {
+ executed = true;
+ }
+ };
+
+ ThreadPool pool { 1 };
+ bool executed = false;
+ Actor<Test> actor(pool, executed);
+
+ actor.ask(&Test::doIt).get();
+ EXPECT_TRUE(executed);
+}
+
TEST(Actor, NoSelfActorRef) {
// Not all actors need a reference to self
-
+
// Trivially constructable
struct Trivial {};
-
+
ThreadPool pool { 2 };
Actor<Trivial> trivial(pool);
-
-
+
+
// With arguments
struct WithArguments {
std::promise<void> promise;
-
+
WithArguments(std::promise<void> promise_)
- : promise(std::move(promise_)) {
+ : promise(std::move(promise_)) {
}
-
+
void receive() {
promise.set_value();
}
};
-
+
std::promise<void> promise;
auto future = promise.get_future();
Actor<WithArguments> withArguments(pool, std::move(promise));
-
+
withArguments.invoke(&WithArguments::receive);
future.wait();
}
diff --git a/test/actor/actor_ref.test.cpp b/test/actor/actor_ref.test.cpp
index 52b0de295b..20aa1c35c1 100644
--- a/test/actor/actor_ref.test.cpp
+++ b/test/actor/actor_ref.test.cpp
@@ -62,6 +62,28 @@ TEST(ActorRef, Ask) {
EXPECT_EQ(30, ref.ask(&Test::echo, 30).get());
}
+TEST(ActorRef, AskVoid) {
+ // Ask waits for void methods
+
+ struct Test {
+ bool& executed;
+
+ Test(bool& executed_) : executed(executed_) {
+ }
+
+ void doIt() {
+ executed = true;
+ }
+ };
+
+ ThreadPool pool { 1 };
+ bool executed = false;
+ Actor<Test> actor(pool, executed);
+ ActorRef<Test> ref = actor.self();
+
+ ref.ask(&Test::doIt).get();
+ EXPECT_TRUE(executed);
+}
TEST(ActorRef, AskOnDestroyedActor) {
// Tests behavior when calling ask() after the
diff --git a/test/api/recycle_map.cpp b/test/api/recycle_map.cpp
new file mode 100644
index 0000000000..8cd622fe79
--- /dev/null
+++ b/test/api/recycle_map.cpp
@@ -0,0 +1,58 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/storage/online_file_source.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+
+TEST(API, RecycleMapUpdateImages) {
+ util::RunLoop loop;
+
+ StubFileSource fileSource;
+ ThreadPool threadPool(4);
+ float pixelRatio { 1 };
+
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
+ auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
+ pixelRatio, fileSource, threadPool, MapMode::Still);
+
+ EXPECT_TRUE(map);
+
+ auto loadStyle = [&](auto markerName, auto markerPath) {
+ auto source = std::make_unique<GeoJSONSource>("geometry");
+ source->setGeoJSON({ Point<double> { 0, 0 } });
+
+ auto layer = std::make_unique<SymbolLayer>("geometry", "geometry");
+ layer->setIconImage({ markerName });
+
+ map->getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ map->getStyle().addSource(std::move(source));
+ map->getStyle().addLayer(std::move(layer));
+ map->getStyle().addImage(std::make_unique<style::Image>(markerName, decodeImage(util::read_file(markerPath)), 1.0));
+ };
+
+ // default marker
+
+ loadStyle("default_marker", "test/fixtures/sprites/default_marker.png");
+ test::checkImage("test/fixtures/recycle_map/default_marker", frontend.render(*map), 0.0006, 0.1);
+
+ // flipped marker
+
+ loadStyle("flipped_marker", "test/fixtures/sprites/flipped_marker.png");
+ test::checkImage("test/fixtures/recycle_map/flipped_marker", frontend.render(*map), 0.0006, 0.1);
+}
diff --git a/test/api/zoom_history.cpp b/test/api/zoom_history.cpp
new file mode 100644
index 0000000000..1b1159bf52
--- /dev/null
+++ b/test/api/zoom_history.cpp
@@ -0,0 +1,71 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/storage/online_file_source.hpp>
+#include <mbgl/style/function/camera_function.hpp>
+#include <mbgl/style/function/exponential_stops.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(API, ZoomHistory) {
+ util::RunLoop loop;
+
+ StubFileSource fileSource;
+ ThreadPool threadPool(4);
+ float pixelRatio { 1 };
+
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
+ auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
+ pixelRatio, fileSource, threadPool, MapMode::Still);
+
+ EXPECT_TRUE(map);
+
+ map->getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+
+ auto source = std::make_unique<GeoJSONSource>("source");
+ source->setGeoJSON({ LineString<double> { { 45, -45 }, { -45, 45 } } });
+ map->getStyle().addSource(std::move(source));
+
+ auto layer = std::make_unique<LineLayer>("layer-1", "source");
+ layer->setLineWidth(CameraFunction<float> { ExponentialStops<float> {{ { 0, 8 }, { 1, 16 } }} });
+ layer->setLineDasharray({ std::vector<float> { 1, 1 } });
+ layer->setLineColor( { Color::black() } );
+ map->getStyle().addLayer(std::move(layer));
+
+ layer = std::make_unique<LineLayer>("layer-2", "source");
+ layer->setLineWidth(CameraFunction<float> { ExponentialStops<float> {{ { 0, 4 }, { 1, 8 } }} });
+ layer->setLineDasharray({ std::vector<float> { 2, 2 } });
+ layer->setLineColor( { Color::red() } );
+ map->getStyle().addLayer(std::move(layer));
+
+ // ZoomHistory.lastIntegerZoom is 1.
+ map->setZoom(1.0);
+ frontend.render(*map);
+
+ map->setZoom(0.0);
+ frontend.render(*map);
+
+ // ZoomHistory.lastIntegerZoom should be 0.
+ map->setZoom(0.5);
+ test::checkImage("test/fixtures/zoom_history", frontend.render(*map), 0.0002);
+
+ map->setZoom(1.0);
+ frontend.render(*map);
+
+ map->setZoom(0.5);
+ test::checkImage("test/fixtures/zoom_history", frontend.render(*map), 0.0002);
+}
diff --git a/test/fixtures/api/empty-zoomed.json b/test/fixtures/api/empty-zoomed.json
new file mode 100644
index 0000000000..02d8fca99e
--- /dev/null
+++ b/test/fixtures/api/empty-zoomed.json
@@ -0,0 +1,6 @@
+{
+ "version": 8,
+ "zoom": 0.5,
+ "sources": {},
+ "layers": []
+}
diff --git a/test/fixtures/map/nocontent/expected.png b/test/fixtures/map/nocontent/expected.png
new file mode 100644
index 0000000000..8fa3f0f5db
--- /dev/null
+++ b/test/fixtures/map/nocontent/expected.png
Binary files differ
diff --git a/test/fixtures/offline_database/v5.db b/test/fixtures/offline_database/v5.db
new file mode 100644
index 0000000000..9bba351208
--- /dev/null
+++ b/test/fixtures/offline_database/v5.db
Binary files differ
diff --git a/test/fixtures/offline_database/v999.db b/test/fixtures/offline_database/v999.db
new file mode 100644
index 0000000000..97c36cce36
--- /dev/null
+++ b/test/fixtures/offline_database/v999.db
Binary files differ
diff --git a/test/fixtures/recycle_map/default_marker/expected.png b/test/fixtures/recycle_map/default_marker/expected.png
new file mode 100644
index 0000000000..a23b79f8d8
--- /dev/null
+++ b/test/fixtures/recycle_map/default_marker/expected.png
Binary files differ
diff --git a/test/fixtures/recycle_map/flipped_marker/expected.png b/test/fixtures/recycle_map/flipped_marker/expected.png
new file mode 100644
index 0000000000..3c4847f3a7
--- /dev/null
+++ b/test/fixtures/recycle_map/flipped_marker/expected.png
Binary files differ
diff --git a/test/fixtures/style_parser/geojson-missing-properties.info.json b/test/fixtures/style_parser/geojson-missing-properties.info.json
new file mode 100644
index 0000000000..9c25a2f488
--- /dev/null
+++ b/test/fixtures/style_parser/geojson-missing-properties.info.json
@@ -0,0 +1,6 @@
+{
+ "default": {
+ "log": [
+ ]
+ }
+}
diff --git a/test/fixtures/style_parser/geojson-missing-properties.style.json b/test/fixtures/style_parser/geojson-missing-properties.style.json
new file mode 100644
index 0000000000..fc4fe97c78
--- /dev/null
+++ b/test/fixtures/style_parser/geojson-missing-properties.style.json
@@ -0,0 +1,9 @@
+{
+ "version": 8,
+ "sources": {
+ "mapbox": {
+ "type": "geojson",
+ "data": { "type": "Feature", "geometry": { "type": "Point", "coordinates": [100.0, 0.0] } }
+ }
+ }
+}
diff --git a/test/fixtures/zoom_history/expected.png b/test/fixtures/zoom_history/expected.png
new file mode 100644
index 0000000000..100dc508a4
--- /dev/null
+++ b/test/fixtures/zoom_history/expected.png
Binary files differ
diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp
index afdbd2a29b..5f9626bc99 100644
--- a/test/gl/bucket.test.cpp
+++ b/test/gl/bucket.test.cpp
@@ -164,15 +164,15 @@ TEST(Buckets, RasterBucketMaskNoChildren) {
(std::vector<RasterLayoutVertex>{
// 1/0/1
RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }),
- RasterProgram::layoutVertex({ 4096, 0 }, { 16384, 0 }),
- RasterProgram::layoutVertex({ 0, 4096 }, { 0, 16384 }),
- RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }),
+ RasterProgram::layoutVertex({ 4096, 0 }, { 4096, 0 }),
+ RasterProgram::layoutVertex({ 0, 4096 }, { 0, 4096 }),
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
// 1/1/1
- RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }),
- RasterProgram::layoutVertex({ 8192, 4096 }, { 32768, 16384 }),
- RasterProgram::layoutVertex({ 4096, 8192 }, { 16384, 32768 }),
- RasterProgram::layoutVertex({ 8192, 8192 }, { 32768, 32768 }),
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
+ RasterProgram::layoutVertex({ 8192, 4096 }, { 8192, 4096 }),
+ RasterProgram::layoutVertex({ 4096, 8192 }, { 4096, 8192 }),
+ RasterProgram::layoutVertex({ 8192, 8192 }, { 8192, 8192 }),
}),
bucket.vertices.vector());
@@ -203,40 +203,40 @@ TEST(Buckets, RasterBucketMaskNoChildren) {
EXPECT_EQ(
(std::vector<RasterLayoutVertex>{
// 1/0/1
- RasterProgram::layoutVertex({ 0, 4096 }, { 0, 16384 }),
- RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }),
- RasterProgram::layoutVertex({ 0, 8192 }, { 0, 32768 }),
- RasterProgram::layoutVertex({ 4096, 8192 }, { 16384, 32768 }),
+ RasterProgram::layoutVertex({ 0, 4096 }, { 0, 4096 }),
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
+ RasterProgram::layoutVertex({ 0, 8192 }, { 0, 8192 }),
+ RasterProgram::layoutVertex({ 4096, 8192 }, { 4096, 8192 }),
// 1/1/0
- RasterProgram::layoutVertex({ 4096, 0 }, { 16384, 0 }),
- RasterProgram::layoutVertex({ 8192, 0 }, { 32768, 0 }),
- RasterProgram::layoutVertex({ 4096, 4096 }, { 16384, 16384 }),
- RasterProgram::layoutVertex({ 8192, 4096 }, { 32768, 16384 }),
+ RasterProgram::layoutVertex({ 4096, 0 }, { 4096, 0 }),
+ RasterProgram::layoutVertex({ 8192, 0 }, { 8192, 0 }),
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
+ RasterProgram::layoutVertex({ 8192, 4096 }, { 8192, 4096 }),
// 2/2/3
- RasterProgram::layoutVertex({ 4096, 6144 }, { 16384, 24576 }),
- RasterProgram::layoutVertex({ 6144, 6144 }, { 24576, 24576 }),
- RasterProgram::layoutVertex({ 4096, 8192 }, { 16384, 32768 }),
- RasterProgram::layoutVertex({ 6144, 8192 }, { 24576, 32768 }),
+ RasterProgram::layoutVertex({ 4096, 6144 }, { 4096, 6144 }),
+ RasterProgram::layoutVertex({ 6144, 6144 }, { 6144, 6144 }),
+ RasterProgram::layoutVertex({ 4096, 8192 }, { 4096, 8192 }),
+ RasterProgram::layoutVertex({ 6144, 8192 }, { 6144, 8192 }),
// 2/3/2
- RasterProgram::layoutVertex({ 6144, 4096 }, { 24576, 16384 }),
- RasterProgram::layoutVertex({ 8192, 4096 }, { 32768, 16384 }),
- RasterProgram::layoutVertex({ 6144, 6144 }, { 24576, 24576 }),
- RasterProgram::layoutVertex({ 8192, 6144 }, { 32768, 24576 }),
+ RasterProgram::layoutVertex({ 6144, 4096 }, { 6144, 4096 }),
+ RasterProgram::layoutVertex({ 8192, 4096 }, { 8192, 4096 }),
+ RasterProgram::layoutVertex({ 6144, 6144 }, { 6144, 6144 }),
+ RasterProgram::layoutVertex({ 8192, 6144 }, { 8192, 6144 }),
// 3/6/7
- RasterProgram::layoutVertex({ 6144, 7168 }, { 24576, 28672 }),
- RasterProgram::layoutVertex({ 7168, 7168 }, { 28672, 28672 }),
- RasterProgram::layoutVertex({ 6144, 8192 }, { 24576, 32768 }),
- RasterProgram::layoutVertex({ 7168, 8192 }, { 28672, 32768 }),
+ RasterProgram::layoutVertex({ 6144, 7168 }, { 6144, 7168 }),
+ RasterProgram::layoutVertex({ 7168, 7168 }, { 7168, 7168 }),
+ RasterProgram::layoutVertex({ 6144, 8192 }, { 6144, 8192 }),
+ RasterProgram::layoutVertex({ 7168, 8192 }, { 7168, 8192 }),
// 3/7/6
- RasterProgram::layoutVertex({ 7168, 6144 }, { 28672, 24576 }),
- RasterProgram::layoutVertex({ 8192, 6144 }, { 32768, 24576 }),
- RasterProgram::layoutVertex({ 7168, 7168 }, { 28672, 28672 }),
- RasterProgram::layoutVertex({ 8192, 7168 }, { 32768, 28672 }),
+ RasterProgram::layoutVertex({ 7168, 6144 }, { 7168, 6144 }),
+ RasterProgram::layoutVertex({ 8192, 6144 }, { 8192, 6144 }),
+ RasterProgram::layoutVertex({ 7168, 7168 }, { 7168, 7168 }),
+ RasterProgram::layoutVertex({ 8192, 7168 }, { 8192, 7168 }),
}),
bucket.vertices.vector());
diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp
index 95d652f2af..9358175297 100644
--- a/test/map/map.test.cpp
+++ b/test/map/map.test.cpp
@@ -109,6 +109,32 @@ TEST(Map, LatLngBoundsToCamera) {
CameraOptions virtualCamera = test.map.cameraForLatLngBounds(bounds, {});
ASSERT_TRUE(bounds.contains(*virtualCamera.center));
+ EXPECT_NEAR(*virtualCamera.zoom, 1.55467, 1e-5);
+}
+
+TEST(Map, LatLngBoundsToCameraWithAngle) {
+ MapTest<> test;
+
+ test.map.setLatLngZoom({ 40.712730, -74.005953 }, 16.0);
+
+ LatLngBounds bounds = LatLngBounds::hull({15.68169,73.499857}, {53.560711, 134.77281});
+
+ CameraOptions virtualCamera = test.map.cameraForLatLngBounds(bounds, {}, 35);
+ ASSERT_TRUE(bounds.contains(*virtualCamera.center));
+ EXPECT_NEAR(*virtualCamera.zoom, 1.21385, 1e-5);
+ EXPECT_DOUBLE_EQ(virtualCamera.angle.value_or(0), -35 * util::DEG2RAD);
+}
+
+TEST(Map, LatLngsToCamera) {
+ MapTest<> test;
+
+ std::vector<LatLng> latLngs{{ 40.712730, 74.005953 }, {15.68169,73.499857}, {30.82678, 83.4082}};
+
+ CameraOptions virtualCamera = test.map.cameraForLatLngs(latLngs, {}, 23);
+ EXPECT_DOUBLE_EQ(virtualCamera.angle.value_or(0), -23 * util::DEG2RAD);
+ EXPECT_NEAR(virtualCamera.zoom.value_or(0), 2.75434, 1e-5);
+ EXPECT_NEAR(virtualCamera.center->latitude(), 28.49288, 1e-5);
+ EXPECT_NEAR(virtualCamera.center->longitude(), 74.97437, 1e-5);
}
TEST(Map, CameraToLatLngBounds) {
@@ -160,6 +186,21 @@ TEST(Map, Offline) {
NetworkStatus::Set(NetworkStatus::Status::Online);
}
+TEST(Map, SetStyleDefaultCamera) {
+ MapTest<> test;
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.0);
+ EXPECT_DOUBLE_EQ(test.map.getPitch(), 0.0);
+ EXPECT_DOUBLE_EQ(test.map.getBearing(), 0.0);
+ EXPECT_EQ(test.map.getLatLng(), LatLng {});
+
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty-zoomed.json"));
+ EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.0);
+
+ test.map.jumpTo(test.map.getStyle().getDefaultCamera());
+ EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.5);
+}
+
TEST(Map, SetStyleInvalidJSON) {
Log::setObserver(std::make_unique<FixtureLogObserver>());
@@ -569,3 +610,45 @@ TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) {
runLoop.run();
}
+
+TEST(Map, NoContentTiles) {
+ MapTest<DefaultFileSource> test {":memory:", "."};
+
+ using namespace std::chrono_literals;
+
+ // Insert a 204 No Content response for the 0/0/0 tile
+ Response response;
+ response.noContent = true;
+ response.expires = util::now() + 1h;
+ test.fileSource.put(Resource::tile("http://example.com/{z}-{x}-{y}.vector.pbf", 1.0, 0, 0, 0,
+ Tileset::Scheme::XYZ),
+ response);
+
+ test.map.getStyle().loadJSON(R"STYLE({
+ "version": 8,
+ "name": "Water",
+ "sources": {
+ "mapbox": {
+ "type": "vector",
+ "tiles": ["http://example.com/{z}-{x}-{y}.vector.pbf"]
+ }
+ },
+ "layers": [{
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "red"
+ }
+ }, {
+ "id": "water",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "water"
+ }]
+ })STYLE");
+
+ test::checkImage("test/fixtures/map/nocontent",
+ test.frontend.render(test.map),
+ 0.0015,
+ 0.1);
+}
diff --git a/test/renderer/backend_scope.test.cpp b/test/renderer/backend_scope.test.cpp
index 06bf1e7750..66cf88a9c6 100644
--- a/test/renderer/backend_scope.test.cpp
+++ b/test/renderer/backend_scope.test.cpp
@@ -12,6 +12,10 @@ public:
void bind() override {
}
+ mbgl::Size getFramebufferSize() const override {
+ return mbgl::Size{};
+ }
+
void activate() override {
if (activateFunction) activateFunction();
}
@@ -87,15 +91,15 @@ TEST(BackendScope, NestedScopes) {
TEST(BackendScope, ChainedScopes) {
bool activatedA = false;
bool activatedB = false;
-
+
StubRendererBackend backendA;
backendA.activateFunction = [&] { activatedA = true; };
backendA.deactivateFunction = [&] { activatedA = false; };
-
+
StubRendererBackend backendB;
backendB.activateFunction = [&] { activatedB = true; };
backendB.deactivateFunction = [&] { activatedB = false; };
-
+
{
BackendScope scopeA { backendA };
ASSERT_TRUE(activatedA);
@@ -107,7 +111,7 @@ TEST(BackendScope, ChainedScopes) {
ASSERT_FALSE(activatedB);
ASSERT_TRUE(activatedA);
}
-
+
ASSERT_FALSE(activatedA);
ASSERT_FALSE(activatedB);
}
diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp
index 5e6da5c005..ebe1bcd72f 100644
--- a/test/renderer/image_manager.test.cpp
+++ b/test/renderer/image_manager.test.cpp
@@ -108,11 +108,12 @@ TEST(ImageManager, RemoveReleasesBinPackRect) {
class StubImageRequestor : public ImageRequestor {
public:
- void onImagesAvailable(ImageMap images) final {
- if (imagesAvailable) imagesAvailable(images);
+ void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID_) final {
+ if (imagesAvailable && imageCorrelationID == imageCorrelationID_) imagesAvailable(images);
}
std::function<void (ImageMap)> imagesAvailable;
+ uint64_t imageCorrelationID = 0;
};
TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) {
@@ -124,7 +125,8 @@ TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) {
notified = true;
};
- imageManager.getImages(requestor, {"one"});
+ uint64_t imageCorrelationID = 0;
+ imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID));
ASSERT_FALSE(notified);
imageManager.setLoaded(true);
@@ -140,8 +142,9 @@ TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) {
notified = true;
};
+ uint64_t imageCorrelationID = 0;
imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2));
- imageManager.getImages(requestor, {"one"});
+ imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID));
ASSERT_TRUE(notified);
}
diff --git a/test/src/mbgl/test/stub_file_source.cpp b/test/src/mbgl/test/stub_file_source.cpp
index 7891d5d907..0bbff84ff3 100644
--- a/test/src/mbgl/test/stub_file_source.cpp
+++ b/test/src/mbgl/test/stub_file_source.cpp
@@ -17,7 +17,12 @@ public:
StubFileSource& fileSource;
};
-StubFileSource::StubFileSource() {
+StubFileSource::StubFileSource(ResponseType type_)
+ : type(type_) {
+ if (type == ResponseType::Synchronous) {
+ return;
+ }
+
timer.start(1ms, 1ms, [this] {
// Explicit copy to avoid iterator invalidation if ~StubFileRequest gets called within the loop.
auto pending_ = pending;
@@ -46,7 +51,14 @@ StubFileSource::~StubFileSource() = default;
std::unique_ptr<AsyncRequest> StubFileSource::request(const Resource& resource, Callback callback) {
auto req = std::make_unique<StubFileRequest>(*this);
- pending.emplace(req.get(), std::make_tuple(resource, response, callback));
+ if (type == ResponseType::Synchronous) {
+ optional<Response> res = response(resource);
+ if (res) {
+ callback(*res);
+ }
+ } else {
+ pending.emplace(req.get(), std::make_tuple(resource, response, callback));
+ }
return std::move(req);
}
diff --git a/test/src/mbgl/test/stub_file_source.hpp b/test/src/mbgl/test/stub_file_source.hpp
index 85118e1a77..6cee8377c6 100644
--- a/test/src/mbgl/test/stub_file_source.hpp
+++ b/test/src/mbgl/test/stub_file_source.hpp
@@ -9,7 +9,12 @@ namespace mbgl {
class StubFileSource : public FileSource {
public:
- StubFileSource();
+ enum class ResponseType {
+ Asynchronous = 0,
+ Synchronous
+ };
+
+ StubFileSource(ResponseType = ResponseType::Asynchronous);
~StubFileSource() override;
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
@@ -36,6 +41,7 @@ private:
optional<Response> defaultResponse(const Resource&);
std::unordered_map<AsyncRequest*, std::tuple<Resource, ResponseFunction, Callback>> pending;
+ ResponseType type;
util::Timer timer;
};
diff --git a/test/storage/default_file_source.test.cpp b/test/storage/default_file_source.test.cpp
index 41e305a692..b5686b5ffe 100644
--- a/test/storage/default_file_source.test.cpp
+++ b/test/storage/default_file_source.test.cpp
@@ -22,6 +22,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheResponse)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response 1", *res.data);
EXPECT_TRUE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
response = res;
@@ -34,6 +35,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheResponse)) {
ASSERT_TRUE(res2.data.get());
EXPECT_EQ(*response.data, *res2.data);
EXPECT_EQ(response.expires, res2.expires);
+ EXPECT_EQ(response.mustRevalidate, res2.mustRevalidate);
EXPECT_EQ(response.modified, res2.modified);
EXPECT_EQ(response.etag, res2.etag);
@@ -51,35 +53,53 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateSame)) {
const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
std::unique_ptr<AsyncRequest> req1;
std::unique_ptr<AsyncRequest> req2;
- uint16_t counter = 0;
+ bool gotResponse = false;
// First request causes the response to get cached.
req1 = fs.request(revalidateSame, [&](Response res) {
req1.reset();
EXPECT_EQ(nullptr, res.error);
+ EXPECT_FALSE(res.notModified);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_EQ("snowfall", *res.etag);
- // Second request returns the cached response, then immediately revalidates.
- req2 = fs.request(revalidateSame, [&, res](Response res2) {
- if (counter == 0) {
- ++counter;
+ // The first response is stored in the cache, but it has 'must-revalidate' set. This means
+ // it can't return the cached response right away and we must wait for the revalidation
+ // request to complete. We can distinguish the cached response from the revalided response
+ // because the latter has an expiration date, while the cached response doesn't.
+ req2 = fs.request(revalidateSame, [&](Response res2) {
+ if (!gotResponse) {
+ // Even though we could find the response in the database, we send a revalidation
+ // request and get a 304 response. Since we haven't sent a reply yet, we're forcing
+ // notModified to be false so that implementations can continue to use the
+ // notModified flag to skip parsing new data.
+ gotResponse = true;
+ EXPECT_EQ(nullptr, res2.error);
EXPECT_FALSE(res2.notModified);
+ ASSERT_TRUE(res2.data.get());
+ EXPECT_EQ("Response", *res2.data);
+ EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
+ EXPECT_FALSE(bool(res2.modified));
+ EXPECT_EQ("snowfall", *res2.etag);
} else {
+ // The test server sends a Cache-Control header with a max-age of 1 second. This
+ // means that our OnlineFileSource implementation will request the tile again after
+ // 1 second. This time, our request already had a prior response, so we don't need
+ // to send the data again, and instead can actually forward the notModified flag.
req2.reset();
-
EXPECT_EQ(nullptr, res2.error);
EXPECT_TRUE(res2.notModified);
- ASSERT_FALSE(res2.data.get());
+ EXPECT_FALSE(res2.data.get());
EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
EXPECT_FALSE(bool(res2.modified));
- // We're not sending the ETag in the 304 reply, but it should still be there.
EXPECT_EQ("snowfall", *res2.etag);
-
loop.stop();
}
});
@@ -96,34 +116,53 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateModified)) {
"http://127.0.0.1:3000/revalidate-modified" };
std::unique_ptr<AsyncRequest> req1;
std::unique_ptr<AsyncRequest> req2;
- uint16_t counter = 0;
+ bool gotResponse = false;
// First request causes the response to get cached.
req1 = fs.request(revalidateModified, [&](Response res) {
req1.reset();
EXPECT_EQ(nullptr, res.error);
+ EXPECT_FALSE(res.notModified);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified);
EXPECT_FALSE(res.etag);
- // Second request returns the cached response, then immediately revalidates.
+ // The first response is stored in the cache, but it has 'must-revalidate' set. This means
+ // it can't return the cached response right away and we must wait for the revalidation
+ // request to complete. We can distinguish the cached response from the revalided response
+ // because the latter has an expiration date, while the cached response doesn't.
req2 = fs.request(revalidateModified, [&, res](Response res2) {
- if (counter == 0) {
- ++counter;
+ if (!gotResponse) {
+ // Even though we could find the response in the database, we send a revalidation
+ // request and get a 304 response. Since we haven't sent a reply yet, we're forcing
+ // notModified to be false so that implementations can continue to use the
+ // notModified flag to skip parsing new data.
+ gotResponse = true;
+ EXPECT_EQ(nullptr, res2.error);
EXPECT_FALSE(res2.notModified);
+ ASSERT_TRUE(res2.data.get());
+ EXPECT_EQ("Response", *res2.data);
+ EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
+ EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res2.modified);
+ EXPECT_FALSE(res2.etag);
} else {
+ // The test server sends a Cache-Control header with a max-age of 1 second. This
+ // means that our OnlineFileSource implementation will request the tile again after
+ // 1 second. This time, our request already had a prior response, so we don't need
+ // to send the data again, and instead can actually forward the notModified flag.
req2.reset();
-
EXPECT_EQ(nullptr, res2.error);
EXPECT_TRUE(res2.notModified);
- ASSERT_FALSE(res2.data.get());
+ EXPECT_FALSE(res2.data.get());
EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res2.modified);
EXPECT_FALSE(res2.etag);
-
loop.stop();
}
});
@@ -139,7 +178,6 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) {
const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
std::unique_ptr<AsyncRequest> req1;
std::unique_ptr<AsyncRequest> req2;
- uint16_t counter = 0;
// First request causes the response to get cached.
req1 = fs.request(revalidateEtag, [&](Response res) {
@@ -149,27 +187,24 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response 1", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_EQ("response-1", *res.etag);
- // Second request returns the cached response, then immediately revalidates.
+ // Second request does not return the cached response, since it had Cache-Control: must-revalidate set.
req2 = fs.request(revalidateEtag, [&, res](Response res2) {
- if (counter == 0) {
- ++counter;
- EXPECT_FALSE(res2.notModified);
- } else {
- req2.reset();
+ req2.reset();
- EXPECT_EQ(nullptr, res2.error);
- ASSERT_TRUE(res2.data.get());
- EXPECT_NE(res.data, res2.data);
- EXPECT_EQ("Response 2", *res2.data);
- EXPECT_FALSE(bool(res2.expires));
- EXPECT_FALSE(bool(res2.modified));
- EXPECT_EQ("response-2", *res2.etag);
+ EXPECT_EQ(nullptr, res2.error);
+ ASSERT_TRUE(res2.data.get());
+ EXPECT_NE(res.data, res2.data);
+ EXPECT_EQ("Response 2", *res2.data);
+ EXPECT_FALSE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
+ EXPECT_FALSE(bool(res2.modified));
+ EXPECT_EQ("response-2", *res2.etag);
- loop.stop();
- }
+ loop.stop();
});
});
@@ -203,6 +238,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(HTTPIssue1369)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -232,6 +268,7 @@ TEST(DefaultFileSource, OptionalNonExpired) {
EXPECT_EQ("Cached value", *res.data);
ASSERT_TRUE(bool(res.expires));
EXPECT_EQ(*response.expires, *res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -261,6 +298,7 @@ TEST(DefaultFileSource, OptionalExpired) {
EXPECT_EQ("Cached value", *res.data);
ASSERT_TRUE(bool(res.expires));
EXPECT_EQ(*response.expires, *res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -301,6 +339,7 @@ TEST(DefaultFileSource, OptionalNotFound) {
EXPECT_EQ("Not found in offline database", res.error->message);
EXPECT_FALSE(res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -334,6 +373,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagNotModified)) {
EXPECT_FALSE(res.data.get());
ASSERT_TRUE(bool(res.expires));
EXPECT_LT(util::now(), *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
ASSERT_TRUE(bool(res.etag));
EXPECT_EQ("snowfall", *res.etag);
@@ -368,6 +408,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagModified)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
ASSERT_TRUE(bool(res.etag));
EXPECT_EQ("snowfall", *res.etag);
@@ -403,6 +444,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheFull)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
ASSERT_TRUE(bool(res.etag));
EXPECT_EQ("snowfall", *res.etag);
@@ -437,6 +479,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedNotModified))
EXPECT_FALSE(res.data.get());
ASSERT_TRUE(bool(res.expires));
EXPECT_LT(util::now(), *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
ASSERT_TRUE(bool(res.modified));
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified);
EXPECT_FALSE(bool(res.etag));
@@ -471,6 +514,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedModified)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified);
EXPECT_FALSE(res.etag);
loop.stop();
@@ -502,6 +546,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -518,6 +563,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -525,3 +571,81 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
loop.run();
}
+
+// Test that a stale cache file that has must-revalidate set will trigger a response.
+TEST(DefaultFileSource, TEST_REQUIRES_SERVER(RespondToStaleMustRevalidate)) {
+ util::RunLoop loop;
+ DefaultFileSource fs(":memory:", ".");
+
+ Resource resource { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
+ resource.necessity = Resource::Necessity::Optional;
+
+ // using namespace std::chrono_literals;
+
+ // Put an existing value in the cache that has expired, and has must-revalidate set.
+ Response response;
+ response.data = std::make_shared<std::string>("Cached value");
+ response.modified = Timestamp(Seconds(1417392000)); // December 1, 2014
+ response.expires = Timestamp(Seconds(1417392000));
+ response.mustRevalidate = true;
+ response.etag.emplace("snowfall");
+ fs.put(resource, response);
+
+ std::unique_ptr<AsyncRequest> req;
+ req = fs.request(resource, [&](Response res) {
+ req.reset();
+ ASSERT_TRUE(res.error.get());
+ EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason);
+ EXPECT_EQ("Cached resource is unusable", res.error->message);
+ EXPECT_FALSE(res.notModified);
+ ASSERT_TRUE(res.data.get());
+ EXPECT_EQ("Cached value", *res.data);
+ ASSERT_TRUE(res.expires);
+ EXPECT_EQ(Timestamp{ Seconds(1417392000) }, *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
+ ASSERT_TRUE(res.modified);
+ EXPECT_EQ(Timestamp{ Seconds(1417392000) }, *res.modified);
+ ASSERT_TRUE(res.etag);
+ EXPECT_EQ("snowfall", *res.etag);
+
+ resource.priorEtag = res.etag;
+ resource.priorModified = res.modified;
+ resource.priorExpires = res.expires;
+ resource.priorData = res.data;
+
+ loop.stop();
+ });
+
+ loop.run();
+
+ // Now run this request again, with the data we gathered from the previous stale/unusable
+ // request. We're replacing the data so that we can check that the DefaultFileSource doesn't
+ // attempt another database access if we already have the value.
+ resource.necessity = Resource::Necessity::Required;
+ resource.priorData = std::make_shared<std::string>("Prior value");
+
+ req = fs.request(resource, [&](Response res) {
+ req.reset();
+ ASSERT_EQ(nullptr, res.error.get());
+ // Since the data was found in the cache, we're doing a revalidation request. Yet, since
+ // this request hasn't returned data before, we're setting notModified to false in the
+ // OnlineFileSource to ensure that requestors know that this is the first time they're
+ // seeing this data.
+ EXPECT_FALSE(res.notModified);
+ ASSERT_TRUE(res.data.get());
+ // Ensure that it's the value that we manually inserted into the cache rather than the value
+ // the server returns, since we should be executing a revalidation request which doesn't
+ // return new data, only a 304 Not Modified response.
+ EXPECT_EQ("Prior value", *res.data);
+ ASSERT_TRUE(res.expires);
+ EXPECT_LE(util::now(), *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
+ ASSERT_TRUE(res.modified);
+ EXPECT_EQ(Timestamp{ Seconds(1417392000) }, *res.modified);
+ ASSERT_TRUE(res.etag);
+ EXPECT_EQ("snowfall", *res.etag);
+ loop.stop();
+ });
+
+ loop.run();
+}
diff --git a/test/storage/http_file_source.test.cpp b/test/storage/http_file_source.test.cpp
index 5b081d7d57..006b7a0fb3 100644
--- a/test/storage/http_file_source.test.cpp
+++ b/test/storage/http_file_source.test.cpp
@@ -26,6 +26,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -44,6 +45,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP404)) {
EXPECT_EQ("HTTP status code 404", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -61,6 +63,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTPTile404)) {
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -78,6 +81,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200EmptyData)) {
EXPECT_FALSE(bool(res.error));
EXPECT_EQ(*res.data, std::string());
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -95,6 +99,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP204)) {
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -113,6 +118,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP500)) {
EXPECT_EQ("HTTP status code 500", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -131,6 +137,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(ExpiresParsing)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_EQ(Timestamp{ Seconds(1420797926) }, res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420794326) }, res.modified);
EXPECT_EQ("foo", *res.etag);
loop.stop();
@@ -148,6 +155,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(CacheControlParsing)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_GT(Seconds(2), util::abs(*res.expires - util::now() - Seconds(120))) << "Expiration date isn't about 120 seconds in the future";
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -176,6 +184,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(Load)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
diff --git a/test/storage/offline.test.cpp b/test/storage/offline.test.cpp
index 0faaabc298..e7ebe5199f 100644
--- a/test/storage/offline.test.cpp
+++ b/test/storage/offline.test.cpp
@@ -52,3 +52,11 @@ TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) {
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
region.tileCover(SourceType::Vector, 512, { 0, 22 }));
}
+
+TEST(OfflineTilePyramidRegionDefinition, TileCount) {
+ OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 22, 1.0);
+
+ //These numbers match the count from tileCover().size().
+ EXPECT_EQ(38424u, region.tileCount(SourceType::Vector, 512, { 10, 18 }));
+ EXPECT_EQ(9675240u, region.tileCount(SourceType::Vector, 512, { 3, 22 }));
+}
diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp
index 872310e46f..d99c1f946f 100644
--- a/test/storage/offline_database.test.cpp
+++ b/test/storage/offline_database.test.cpp
@@ -634,24 +634,35 @@ static int databaseSyncMode(const std::string& path) {
return stmt.get<int>(0);
}
+static std::vector<std::string> databaseTableColumns(const std::string& path, const std::string& name) {
+ mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly);
+ const auto sql = std::string("pragma table_info(") + name + ")";
+ mapbox::sqlite::Statement stmt = db.prepare(sql.c_str());
+ std::vector<std::string> columns;
+ while (stmt.run()) {
+ columns.push_back(stmt.get<std::string>(1));
+ }
+ return columns;
+}
+
TEST(OfflineDatabase, MigrateFromV2Schema) {
using namespace mbgl;
// v2.db is a v2 database containing a single offline region with a small number of resources.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v2.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v2.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
- EXPECT_LT(databasePageCount("test/fixtures/offline_database/v5.db"),
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+ EXPECT_LT(databasePageCount("test/fixtures/offline_database/migrated.db"),
databasePageCount("test/fixtures/offline_database/v2.db"));
}
@@ -660,18 +671,18 @@ TEST(OfflineDatabase, MigrateFromV3Schema) {
// v3.db is a v3 database, migrated from v2.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v3.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v3.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
}
TEST(OfflineDatabase, MigrateFromV4Schema) {
@@ -679,22 +690,74 @@ TEST(OfflineDatabase, MigrateFromV4Schema) {
// v4.db is a v4 database, migrated from v2 & v3. This database used `journal_mode = WAL` and `synchronous = NORMAL`.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v4.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v4.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
// Journal mode should be DELETE after migration to v5.
- EXPECT_EQ("delete", databaseJournalMode("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ("delete", databaseJournalMode("test/fixtures/offline_database/migrated.db"));
// Synchronous setting should be FULL (2) after migration to v5.
- EXPECT_EQ(2, databaseSyncMode("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(2, databaseSyncMode("test/fixtures/offline_database/migrated.db"));
+}
+
+
+TEST(OfflineDatabase, MigrateFromV5Schema) {
+ using namespace mbgl;
+
+ // v5.db is a v5 database, migrated from v2, v3 & v4.
+
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v5.db"));
+
+ {
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
+ auto regions = db.listRegions();
+ for (auto& region : regions) {
+ db.deleteRegion(std::move(region));
+ }
+ }
+
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+
+ EXPECT_EQ((std::vector<std::string>{ "id", "url_template", "pixel_ratio", "z", "x", "y",
+ "expires", "modified", "etag", "data", "compressed",
+ "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "tiles"));
+ EXPECT_EQ((std::vector<std::string>{ "id", "url", "kind", "expires", "modified", "etag", "data",
+ "compressed", "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "resources"));
+}
+
+TEST(OfflineDatabase, DowngradeSchema) {
+ using namespace mbgl;
+
+ // v999.db is a v999 database, it should be deleted
+ // and recreated with the current schema.
+
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v999.db"));
+
+ {
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
+ }
+
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+
+ EXPECT_EQ((std::vector<std::string>{ "id", "url_template", "pixel_ratio", "z", "x", "y",
+ "expires", "modified", "etag", "data", "compressed",
+ "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "tiles"));
+ EXPECT_EQ((std::vector<std::string>{ "id", "url", "kind", "expires", "modified", "etag", "data",
+ "compressed", "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "resources"));
}
diff --git a/test/storage/online_file_source.test.cpp b/test/storage/online_file_source.test.cpp
index 1a1d2d42f8..70bfe3ac95 100644
--- a/test/storage/online_file_source.test.cpp
+++ b/test/storage/online_file_source.test.cpp
@@ -36,6 +36,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(CancelMultiple)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -62,6 +63,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(TemporaryError)) {
EXPECT_EQ("HTTP status code 500", res.error->message);
ASSERT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
} break;
@@ -73,6 +75,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(TemporaryError)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -99,6 +102,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(ConnectionError)) {
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
@@ -126,6 +130,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Timeout)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_TRUE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter == 4) {
@@ -150,6 +155,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RetryDelayOnExpiredTile)) {
counter++;
EXPECT_EQ(nullptr, res.error);
EXPECT_GT(util::now(), *res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
});
util::Timer timer;
@@ -170,6 +176,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RetryOnClockSkew)) {
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/clockskew" };
std::unique_ptr<AsyncRequest> req1 = fs.request(resource, [&](Response res) {
+ EXPECT_FALSE(res.mustRevalidate);
switch (counter++) {
case 0: {
EXPECT_EQ(nullptr, res.error);
@@ -240,6 +247,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Load)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
@@ -277,6 +285,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChange)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -315,6 +324,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChangePreempt)) {
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
diff --git a/test/storage/server.js b/test/storage/server.js
index b54ff835ec..d6429e4635 100755
--- a/test/storage/server.js
+++ b/test/storage/server.js
@@ -44,8 +44,8 @@ app.get('/cache', function(req, res) {
app.get('/revalidate-same', function(req, res) {
if (req.headers['if-none-match'] == 'snowfall') {
- // Second request can be cached for 30 seconds.
- res.setHeader('Cache-Control', 'max-age=30');
+ // Second request can be cached for 1 second.
+ res.setHeader('Cache-Control', 'max-age=1, must-revalidate');
res.status(304).end();
} else {
// First request must always be revalidated.
@@ -67,7 +67,7 @@ app.get('/revalidate-modified', function(req, res) {
if (req.headers['if-modified-since']) {
var modified_since = new Date(req.headers['if-modified-since']);
if (modified_since >= jan1) {
- res.setHeader('Cache-Control', 'max-age=30');
+ res.setHeader('Cache-Control', 'max-age=1, must-revalidate');
res.status(304).end();
return;
}
diff --git a/test/style/conversion/geojson_options.test.cpp b/test/style/conversion/geojson_options.test.cpp
index e6bd984f36..a798ad6559 100644
--- a/test/style/conversion/geojson_options.test.cpp
+++ b/test/style/conversion/geojson_options.test.cpp
@@ -33,6 +33,7 @@ TEST(GeoJSONOptions, RetainsDefaults) {
GeoJSONOptions defaults;
// GeoJSON-VT
+ ASSERT_EQ(converted.minzoom, defaults.minzoom);
ASSERT_EQ(converted.maxzoom, defaults.maxzoom);
ASSERT_EQ(converted.buffer, defaults.buffer);
ASSERT_EQ(converted.tolerance, defaults.tolerance);
@@ -61,6 +62,7 @@ TEST(GeoJSONOptions, FullConversion) {
GeoJSONOptions converted = *convert<GeoJSONOptions>(raw, error);
// GeoJSON-VT
+ ASSERT_EQ(converted.minzoom, 0);
ASSERT_EQ(converted.maxzoom, 1);
ASSERT_EQ(converted.buffer, 2);
ASSERT_EQ(converted.tolerance, 3);
diff --git a/test/style/style.test.cpp b/test/style/style.test.cpp
index ab58eb1024..9bdab37ac6 100644
--- a/test/style/style.test.cpp
+++ b/test/style/style.test.cpp
@@ -28,27 +28,27 @@ TEST(Style, Properties) {
style.loadJSON(R"STYLE({"center": [10, 20]})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ((LatLng{20, 10}), style.getDefaultLatLng());
+ ASSERT_EQ((LatLng{20, 10}), *style.getDefaultCamera().center);
style.loadJSON(R"STYLE({"bearing": 24})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ((LatLng{0, 0}), style.getDefaultLatLng());
- ASSERT_EQ(24, style.getDefaultBearing());
+ ASSERT_EQ(LatLng {}, *style.getDefaultCamera().center);
+ ASSERT_EQ(24, *style.getDefaultCamera().angle);
style.loadJSON(R"STYLE({"zoom": 13.3})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ(13.3, style.getDefaultZoom());
+ ASSERT_EQ(13.3, *style.getDefaultCamera().zoom);
style.loadJSON(R"STYLE({"pitch": 60})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ(60, style.getDefaultPitch());
+ ASSERT_EQ(60, *style.getDefaultCamera().pitch);
style.loadJSON(R"STYLE({"name": 23, "center": {}, "bearing": "north", "zoom": null, "pitch": "wide"})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ((LatLng{0, 0}), style.getDefaultLatLng());
- ASSERT_EQ(0, style.getDefaultBearing());
- ASSERT_EQ(0, style.getDefaultZoom());
- ASSERT_EQ(0, style.getDefaultPitch());
+ ASSERT_EQ(LatLng {}, *style.getDefaultCamera().center);
+ ASSERT_EQ(0, *style.getDefaultCamera().zoom);
+ ASSERT_EQ(0, *style.getDefaultCamera().angle);
+ ASSERT_EQ(0, *style.getDefaultCamera().pitch);
}
TEST(Style, DuplicateSource) {
diff --git a/test/text/glyph_loader.test.cpp b/test/text/glyph_loader.test.cpp
index be197ebb46..20ac045925 100644
--- a/test/text/glyph_loader.test.cpp
+++ b/test/text/glyph_loader.test.cpp
@@ -214,3 +214,52 @@ TEST(GlyphManager, LoadingInvalid) {
{{{"Test Stack"}}, {u'A', u'E'}}
});
}
+
+TEST(GlyphManager, ImmediateFileSource) {
+ class GlyphManagerTestSynchronous {
+ public:
+ util::RunLoop loop;
+ StubFileSource fileSource = { StubFileSource::ResponseType::Synchronous };
+ StubGlyphManagerObserver observer;
+ StubGlyphRequestor requestor;
+ GlyphManager glyphManager { fileSource };
+
+ void run(const std::string& url, GlyphDependencies dependencies) {
+ // Squelch logging.
+ Log::setObserver(std::make_unique<Log::NullObserver>());
+
+ glyphManager.setURL(url);
+ glyphManager.setObserver(&observer);
+ glyphManager.getGlyphs(requestor, std::move(dependencies));
+
+ loop.run();
+ }
+
+ void end() {
+ loop.stop();
+ }
+ };
+
+ GlyphManagerTestSynchronous test;
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/resources/glyphs.pbf"));
+ return response;
+ };
+
+ test.observer.glyphsError = [&] (const FontStack&, const GlyphRange&, std::exception_ptr) {
+ FAIL();
+ test.end();
+ };
+
+ test.requestor.glyphsAvailable = [&] (GlyphMap) {
+ test.end();
+ };
+
+ test.run(
+ "test/fixtures/resources/glyphs.pbf",
+ GlyphDependencies {
+ {{{"Test Stack"}}, {u'a', u'å', u' '}}
+ });
+}
diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp
index 682ba9d795..8eedd9bd2e 100644
--- a/test/text/quads.test.cpp
+++ b/test/text/quads.test.cpp
@@ -17,7 +17,7 @@ TEST(getIconQuads, normal) {
style::Image::Impl("test", PremultipliedImage({1,1}), 1.0)
};
- auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, 0);
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
Shaping shapedText;
@@ -42,7 +42,7 @@ TEST(getIconQuads, style) {
style::Image::Impl("test", PremultipliedImage({1,1}), 1.0)
};
- auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, 0);
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
Shaping shapedText;
@@ -50,7 +50,7 @@ TEST(getIconQuads, style) {
shapedText.bottom = 30.0f;
shapedText.left = -60.0f;
shapedText.right = 20.0f;
- shapedText.positionedGlyphs.emplace_back(PositionedGlyph(32, 0.0f, 0.0f, 0));
+ shapedText.positionedGlyphs.emplace_back(PositionedGlyph(32, 0.0f, 0.0f, false));
// none
{
diff --git a/test/tile/annotation_tile.test.cpp b/test/tile/annotation_tile.test.cpp
index c58b980ecd..8f3f903925 100644
--- a/test/tile/annotation_tile.test.cpp
+++ b/test/tile/annotation_tile.test.cpp
@@ -4,7 +4,6 @@
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/renderer/render_style.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/text/collision_tile.hpp>
@@ -31,7 +30,6 @@ public:
AnnotationManager annotationManager { style };
HeadlessBackend backend;
BackendScope scope { backend };
- RenderStyle renderStyle { threadPool, fileSource };
ImageManager imageManager;
GlyphManager glyphManager { fileSource };
@@ -62,8 +60,7 @@ TEST(AnnotationTile, Issue8289) {
std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
std::make_unique<FeatureIndex>(),
std::move(data),
- 0
- });
+ }, 0);
auto collisionTile = std::make_unique<CollisionTile>(PlacementConfig());
@@ -77,23 +74,21 @@ TEST(AnnotationTile, Issue8289) {
std::move(collisionTile),
{},
{},
- 0
- });
+ }, 0);
// Simulate a second layout with empty data.
tile.onLayout(GeometryTile::LayoutResult {
std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
std::make_unique<FeatureIndex>(),
std::make_unique<AnnotationTileData>(),
- 0
- });
+ }, 0);
std::unordered_map<std::string, std::vector<Feature>> result;
GeometryCoordinates queryGeometry {{ Point<int16_t>(0, 0) }};
TransformState transformState;
RenderedQueryOptions options;
- tile.queryRenderedFeatures(result, queryGeometry, transformState, test.renderStyle, options);
+ tile.queryRenderedFeatures(result, queryGeometry, transformState, {}, options);
EXPECT_TRUE(result.empty());
}
diff --git a/test/tile/geojson_tile.test.cpp b/test/tile/geojson_tile.test.cpp
index 31fb8c1fd0..953c3b8a5f 100644
--- a/test/tile/geojson_tile.test.cpp
+++ b/test/tile/geojson_tile.test.cpp
@@ -77,3 +77,41 @@ TEST(GeoJSONTile, Issue7648) {
test.loop.runOnce();
}
}
+
+// Tests that tiles remain renderable if they have been renderable and then had an error sent to
+// them, e.g. when revalidating/refreshing the request.
+TEST(GeoJSONTile, Issue9927) {
+ GeoJSONTileTest test;
+
+ CircleLayer layer("circle", "source");
+
+ mapbox::geometry::feature_collection<int16_t> features;
+ features.push_back(mapbox::geometry::feature<int16_t> {
+ mapbox::geometry::point<int16_t>(0, 0)
+ });
+
+ GeoJSONTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, features);
+
+ tile.setLayers({{ layer.baseImpl }});
+ tile.setPlacementConfig({});
+
+ while (!tile.isComplete()) {
+ test.loop.runOnce();
+ }
+
+ ASSERT_TRUE(tile.isRenderable());
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
+
+ // Make sure that once we've had a renderable tile and then receive erroneous data, we retain
+ // the previously rendered data and keep the tile renderable.
+ tile.setError(std::make_exception_ptr(std::runtime_error("Connection offline")));
+ ASSERT_TRUE(tile.isRenderable());
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
+
+ // Then simulate a parsing failure and make sure that we keep it renderable in this situation
+ // as well. We're using 3 as a correlationID since we've done two three calls that increment
+ // this counter (as part of the GeoJSONTile constructor, setLayers, and setPlacementConfig).
+ tile.onError(std::make_exception_ptr(std::runtime_error("Parse error")), 3);
+ ASSERT_TRUE(tile.isRenderable());
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
+ }
diff --git a/test/tile/raster_tile.test.cpp b/test/tile/raster_tile.test.cpp
index e96e09bacf..8b2b3dee61 100644
--- a/test/tile/raster_tile.test.cpp
+++ b/test/tile/raster_tile.test.cpp
@@ -53,7 +53,7 @@ TEST(RasterTile, setError) {
TEST(RasterTile, onError) {
RasterTileTest test;
RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
- tile.onError(std::make_exception_ptr(std::runtime_error("test")));
+ tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0);
EXPECT_FALSE(tile.isRenderable());
EXPECT_TRUE(tile.isLoaded());
EXPECT_TRUE(tile.isComplete());
@@ -62,16 +62,30 @@ TEST(RasterTile, onError) {
TEST(RasterTile, onParsed) {
RasterTileTest test;
RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
- tile.onParsed(std::make_unique<RasterBucket>(PremultipliedImage{}));
+ tile.onParsed(std::make_unique<RasterBucket>(PremultipliedImage{}), 0);
EXPECT_TRUE(tile.isRenderable());
EXPECT_TRUE(tile.isLoaded());
EXPECT_TRUE(tile.isComplete());
+
+ // Make sure that once we've had a renderable tile and then receive erroneous data, we retain
+ // the previously rendered data and keep the tile renderable.
+ tile.setError(std::make_exception_ptr(std::runtime_error("Connection offline")));
+ EXPECT_TRUE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
+
+ // Then simulate a parsing failure and make sure that we keep it renderable in this situation
+ // as well.
+ tile.onError(std::make_exception_ptr(std::runtime_error("Parse error")), 0);
+ ASSERT_TRUE(tile.isRenderable());
+ EXPECT_TRUE(tile.isLoaded());
+ EXPECT_TRUE(tile.isComplete());
}
TEST(RasterTile, onParsedEmpty) {
RasterTileTest test;
RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
- tile.onParsed(nullptr);
+ tile.onParsed(nullptr, 0);
EXPECT_FALSE(tile.isRenderable());
EXPECT_TRUE(tile.isLoaded());
EXPECT_TRUE(tile.isComplete());
diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp
index 45eab21576..7e8b659b7a 100644
--- a/test/tile/vector_tile.test.cpp
+++ b/test/tile/vector_tile.test.cpp
@@ -59,7 +59,8 @@ TEST(VectorTile, setError) {
TEST(VectorTile, onError) {
VectorTileTest test;
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset);
- tile.onError(std::make_exception_ptr(std::runtime_error("test")));
+ tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0);
+
EXPECT_FALSE(tile.isRenderable());
EXPECT_TRUE(tile.isLoaded());
EXPECT_TRUE(tile.isComplete());
@@ -86,16 +87,14 @@ TEST(VectorTile, Issue7615) {
nullptr,
{},
{},
- 0
- });
+ }, 0);
// Subsequent onLayout should not cause the existing symbol bucket to be discarded.
tile.onLayout(GeometryTile::LayoutResult {
std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
nullptr,
nullptr,
- 0
- });
+ }, 0);
EXPECT_EQ(symbolBucket.get(), tile.getBucket(*symbolLayer.baseImpl));
}
diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp
index c746e6dab5..933c18b5ea 100644
--- a/test/util/tile_cover.test.cpp
+++ b/test/util/tile_cover.test.cpp
@@ -84,3 +84,12 @@ TEST(TileCover, SanFranciscoZ0Wrapped) {
EXPECT_EQ((std::vector<UnwrappedTileID>{ { 0, 1, 0 } }),
util::tileCover(sanFranciscoWrapped, 0));
}
+
+TEST(TileCount, SanFranciscoZ10) {
+ EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10, util::tileSize));
+}
+
+TEST(TileCount, SanFranciscoZ22) {
+ EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22, util::tileSize));
+}
+